2018年论文题,以下是论文前3章主要内容,与原题解相关部分为第4章中的启发式合并,也可快速跳至原题解

1.复杂度分析

Treap

定理1:$n$个节点的Treap的期望深度为$o(\log n)$

证明1:假设所有元素从小到大依次为$a_{1},a_{2},...,a_{n}$(不妨假设所有元素各不相同,若有相同可以将这些元素存在同一个位置上),则对于$x$和$y$,分类讨论:

1.若$x\le y$,则$x$是$y$的祖先等价于$a_{x}<\min_{x<j\le y}a_{j}$

2.若$x>y$,则$x$是$y$的祖先等价于$a_{x}<\min_{y\le j<x}a_{j}$

(这里要求随机权值构成小根堆,且不妨假设随机权值各不相同)

这件事的概率也就是$x$权值最小的概率,即$\frac{1}{|x-y|+1}$

$x$的深度也可以定义为$x$的祖先数,而其期望祖先数即为$\sum_{i=1}^{n}\frac{1}{|x-i|+1}\sim o(\log n)$

这是论文中给出的证明,但其实并不太严谨,这里再给出一种证明方式——

证明2:令$T(n)$为$n$个节点的Treap的期望深度,考虑枚举其中随机权值最小的点(作为根),即
$$
T(n)=n+\frac{\sum_{i=1}^{n}T(i-1)+T(n-i)}{n}=n+\frac{2}{n}\sum_{i=0}^{n-1}T(i)
$$
类似地,将$n-1$的式子写出后两式相减,即
$$
T(n)-T(n-1)=1+\frac{2}{n}\sum_{i=0}^{n-1}T(i)-\frac{2}{n-1}\sum_{i=0}^{n-2}T(i)\le 1+\frac{2}{n}T(n-1)
$$
简单化简后,即
$$
\frac{T(n)}{n+1}\le \frac{1}{n+1}+\frac{T(n-1)}{n}\le \frac{1}{n+1}+\frac{1}{n}+\frac{T(n-2)}{n-1}\le ...\le \sum_{i=2}^{n+1}\frac{1}{i}\sim o(\log n)
$$
于是,即$T(n)\sim o(n\log n)$,即所求证

Splay

定理2:对一个$V$个节点的伸展树执行$n$次Splay操作的复杂度为$o((V+n)\log V)$

证明:对于一棵伸展树$T$,定义节点$x$的势能函数为$r(x)=\log sz_{x}$(其中$sz_{x}$为其子树大小),则$T$的势能函数为$\varphi(T)=\sum_{x}r(x)$(其中$x$为$T$中的节点)

假设第$i$次操作后的Splay为$T_{i}$(特别的,$T_{0}$为初始的伸展树),第$i$次操作复杂度(旋转次数)为$a_{i}$,则第$i$次操作的均摊复杂度记为$b_{i}=a_{i}+R(T_{i})-R(T_{i-1})$,后者记为$\Delta_{\varphi}(i)$

综上,总复杂度即
$$
\sum_{i=1}^{n}a_{i}=\sum_{i=1}^{n}b_{i}+R(T_{0})-R(T_{n})\le \sum_{i=1}^{n}b_{i}+V\log V
$$
下面,我们只需要考虑$b_{i}$即可,对Splay中的三类情况分开讨论:

(为了方便,假设执行$Splay(k)$,且$fa$为$k$的父亲,$ga$为$k$的祖父,带$'$为旋转后的$T_{i}$)

1.单旋,其使得$b_{i}$增加$1+r'(fa)-r(k)\sim o(\log V)$

2.三点一线时,先旋转$fa$,再旋转$k$,其使得$b_{i}$增加
$$
1+r'(fa)+r'(ga)-r(k)-r(fa)\le 1+r'(k)+r'(ga)-2r(k)
$$
根据$sz'_{k}=sz_{k}+sz'_{ga}+1$,有$2r'(k)-r(k)-r'(ga)\ge \log \frac{(sz_{k}+{sz'_{ga}})^{2}}{sz_{k}sz'_{ga}}\ge 2$

将$2r'(k)-r(k)-r'(ga)-2\ge 0$加入上式,即有
$$
1+r'(k)+r'(ga)-2r(k)+2r'(k)-r(k)-r'(ga)-2\le 3(r'(k)-r(k))
$$
3.三点不一线时,旋转两次$k$,类似地也可以证明其使得$b_{i}$增加不超过$3(r'(k)-r(k))$

注意到$r'(k)$可以与下一次的$r(k)$相消,因此第2类和第3类总增加量不超过$3(r'(k)-r(k))\sim o(\log V)$

综上,即对于初始为0的$b_{i}$,增加总量也不超过$o(\log V)$,即$b_{i}\sim o(\log V)$

将之代入,不难得到复杂度为$o((V+n)\log V)$,即所求证

2.Treap的可持久化和树套树

可持久化

所有非均摊的数据结构基本都是可以可持久化的

论文中提到了关于Treap中可持久化不能直接复制随机值,而是在比较时进行随机判定是否旋转,并且旋转$k$的概率为$\frac{sz_{k}}{sz_{fa}}$($fa$为$k$的父亲)

(我觉得或许直接复制随机值应该也是对的吧)

树套树

事实上,旋转的Treap也是可以实现树套树的

定理3:若一次旋转$k$的复杂度为$o(sz_{k})$,则Treap单次插入操作期望复杂度为$o(\log n)$

证明:假设插入$k$,当$k$旋转后$k$必然是其子树中权值最小的点,这样的概率是$\frac{1}{sz_{k}}$,那么这次旋转的期望复杂度即为$o(1)$,也可以看作这个节点对答案的期望贡献为$o(1)$

又因为前面说明树高为期望$o(\log n)$,而只有$k$到根路径上的点对答案会有期望$o(1)$的贡献,总复杂度即期望$o(\log n)$,结论成立

定理4:若一次重构$k$子树时间复杂度$o(sz_{k}\log sz_{k})$,则Treap单次删除期望复杂度为$o(\log n)$

证明:由于Treap是随机的,删除节点的子树大小可以看作一个节点的期望子树大小

由于树高$o(\log n)$,因此所有节点子树大小之和为$o(n\log n)$,显然期望子树大小为$o(\log n)$,将其子树暴力重构即可,对于$o(\log\log n)$的复杂度可以忽略,即复杂度为期望$o(\log n)$

上面所给的旋转以及重构的复杂度,也就是平衡树套序列的复杂度(每一个节点维护子树内所有元素所构成的序列),也就可以做到$o(n\log n)$

而对于平衡树套权值线段树,此时插入和删除相较于上面两者也就多了一个$\log n$,复杂度即$o(n\log^{2}n)$,与替罪羊树相同,也是可以接受的

(树套树中的线段树不能互相嵌套,因此重构不能使用线段树合并,仍是$o(sz_{k}\log sz_{k}\log n)$的)

3.Finger Search

Finger Search

关于Finger Search,即在一个数据结构中,令$d(x,y)$为在$x$和$y$之间的元素个数(包括$x$和$y$),当已经确定$x$的位置后,可以在$o(\log d(x,y))$的时间内快速查询$y$的操作

(另外下面还将考虑Finger Search的简单拓展,即快速插入和删除)

显然并不是所有数据结构都能支持Finger Search,下面分别来考虑Treap和Splay

Treap

定理5:在Treap上,$x$到$y$的路径长度为期望$o(\log d(x,y))$

证明:注意到$x$到$y$的路径长度即是$x$祖先且不是$y$祖先的节点数+是$y$祖先且不是$x$祖先的节点数,根据对称性可以仅考虑前者

假设Treap所有元素从小到大依次为$a_{1},a_{2},...,a_{n}$,其中$a_{i}=x$且$a_{j}=y$,显然$d(x,y)=j-i+1$

考虑一个节点$a_{k}$,求出其满足“是$x$祖先且不是$y$祖先”的概率,将其累加即可

根据前面定理1的证明,我们需要对$k$分类讨论:

1.$1\le k\le i$,这等价于$k$是$[k,i]$的最小值且$[k,i]$的最小值大于$(i,j]$的最小值,前者概率为$\frac{1}{i-k+1}$,后者概率为$\frac{j-i}{j-k+1}$,相乘后即$\frac{j-i}{(i-k+1)(j-k+1)}$

注意到这就是$\frac{1}{i-k+1}-\frac{1}{j-k+1}$,累加后即
$$
\sum_{k=1}^{i}\frac{1}{k}-\sum_{k=j-i+1}^{j}\frac{1}{k}=\sum_{k=1}^{i}\frac{1}{k}-(\sum_{k=1}^{j}\frac{1}{k}-\sum_{k=1}^{j-i}\frac{1}{k})\le \sum_{k=1}^{j-i}\frac{1}{k}\sim o(\log d(i,j))
$$
2.$i<k<j$,类似地概率为$\frac{j-k}{(k-i+1)(j-i+1)}=\frac{1}{k-i+1}-\frac{1}{j-i+1}$,累加后即$\sum_{k=1}^{j-i+1}\frac{1}{k}-1\sim o(\log d(i,j))$

由此,在Treap上再维护一个子树最小值和最大值,暴力从$x$向父亲爬去找$y$即可实现Finger Search

下面来考虑Finger Search的拓展,对于插入:根据定理3,可以发现Treap插入的瓶颈事实上就在于寻找位置,更具体的,我们有以下定理——

定理6:Treap单次插入操作期望旋转次数为$o(1)$

证明:根据定理3的证明,一个点期望旋转次数,即其到根所有节点子树大小倒数之和

而所有点期望旋转次数之和,考虑一个点的贡献,不难发现恰好为1,即总和期望为$o(n)$,那么其中一点期望次数为$o(1)$,即所求证

那么找到位置后,再以$o(1)$次旋转即可,即实现了Finger Search的插入

对于删除:Treap的删除需要将该节点旋转到子树叶子,根据定理4这一部分也就是$o(\log\log n)$,也可以忽略,那么同样也可以支持删除

综上,Treap需要通过一些技巧来支持Finger Search即其拓展

Splay

在Splay中,由于每一次会将上次所插入、删除或查询的Splay到根,实际上也就是实现了Finger Search,更具体的来说,可以有以下结论——

定理7(Dynamic Finger Theorem):对于一个$n$个点的Splay,进行$m$次操作,每一次操作的元素为$a_{i}$(特别的,$a_{0}$定义为初始Splay中的根),则复杂度为$o(n+m+\sum_{i=1}^{m}\log d(a_{i-1},a_{i}))$

这个定理的证明比较复杂,这里就省略了

根据这个结论,也就说明Splay可以不需要附加其他操作来支持Finger Search即其拓展

4.Treap的快速合并和分裂

合并

考虑合并两颗Treap,分别为$T_{1}$和$T_{2}$(假设大小分别为$n$和$m$,且$T_{1}$中的权值严格小于$T_{2}$),普通的合并也就是FHQ Treap,合并复杂度为$o(\log n+\log m)$

事实上,Treap的合并还可以进一步优化,达到$o(\log \min(n,m))$的复杂度

具体来说(不妨假设$n>m$),在合并的递归过程中,一开始会又连续较多次都是以$T_{1}$的根为根并将右儿子与$T_{2}$合并,这些过程完全可以将最后一次与$T_{2}$合并

更具体的,考虑从$T_{1}$中的最大值开始(也就是从根不断向右儿子移动),不断向父亲移动,直至父亲的随机权值小于$T_{2}$根节点的随机权值时停止,并将当前节点与$T_{2}$合并并作为父亲的右儿子

(这里的合并就是普通的合并,即做到$o(\log n+\log m)$的复杂度)

$m$显然是不变的,现在来考虑$\log n$,也可以看作最大值移动的次数

最终这个位置将会被$T_{2}$的根替代,而这条链并不会被压缩,也就是说最终合并后$T_{2}$的根到$T_{1}$中最大值的路径长度严格大于移动的次数,根据定理5可以得到是期望$o(\log m)$的

$n<m$类似,也就是会有较多次将$T_{1}$与$T_{2}$的左儿子合并,同样可以证明复杂度为期望$o(\log n)$

(当然,具体实现中可以更方便的直接自底向上进行合并,这里只是为了说明其实际意义)

分裂

对于一个大小为$n+m$的Treap(记作$T$),普通的分裂也是FHQ Treap,分裂复杂度为$o(\log (n+m))$

Treap的分裂也可以优化,假设拆出两颗大小为$n$和$m$的子树(分别为$T_{1}$和$T_{2}$,且$T_{1}$中的权值严格小于$T_{2}$),则分裂也可以优化到$o(\log \min(n,m))$的复杂度

类似合并,在分裂的过程中,会有很长时间都在向同一边拆分

更具体的,分裂有两种,先来考虑给出排名(也就是$n$和$m$)的方式:

不妨假设$n>m$,从最大值出发去找到值所在的位置,根据定理5可以在$o(\log m)$的时间内找到对应权值以及位置,同时还可以求出最大值与该权值的lca

在这个lca之前,显然都会被分到$T_{1}$,因此可以直接在这个lca内部进行划分

此时递归的深度即与最后$T_{2}$的深度相同,为期望$o(\log m)$

$n<m$也是类似的,从最小值去找该值即可

但如果分裂给出的是权值,由于无法确定$n$和$m$的关系,仅是查找该节点就会退化为$o(\log (n+m))$

这时候还有一个方法,从最小值和最大值同时去找这个权值,且双方每一次各移动一步,那么找到时所花的步数也就是期望$o(\log \min(n,m))$,也就与其相同

启发式合并

启发式合并就不能保证权值有严格的关系,因此对于这样两颗大小为$n$和$m$的Treap(不妨假设$n>m$),通常都是以$o(m\log n)$的复杂度来完成合并的

总得来说,将$n$个大小为1的Treap以此法启发式合并的复杂度为$o(n\log^{2}n)$

但是,我们可以将较小的Treap中的权值从小到大插入,此时借助Finger Search的插入拓展,似乎可以优化复杂度,具体来说有以下定理——

定理8:以上述方式启发式合并$n$个大小为1的Treap,复杂度为$o(n\log n)$

证明:先来考虑以此法合并大小为$n$和$m$($n>m$)的Treap的复杂度,也就可以看作求将$n$划分为若干个数,每一个数的$\log$之和

根据$\log$函数的凸性,不难调整证明均匀划分时复杂度最低,即单次插入复杂度为$o(\log \frac{n}{m})$

接下来,考虑每一个数的贡献:

假设其执行插入操作$k$次,第$i$次插入到的子树大小(指合并结束后)依次为$a_{i}$(特别的,$a_{0}=1$),由于$a_{i-1}$恰好是上一次的$m$,因此其贡献即$\sum_{i=1}^{k}\log\frac{a_{i}}{a_{i-1}}=\log a_{k}\sim o(\log n)$

所有节点贡献之和即为$o(n\log n)$,也就是复杂度

关于Splay

在上述操作中,Splay也可以支持部分操作:

1.快速合并,将较小的Splay的最小或最大值Splay到根,虽然这样合并看上去会增加$\log \max(n,m)$的势能,但如果我们修改势能的定义,将其定义为所有非根节点势能和,还是可以做到均摊$o(\log \min(n,m))$的复杂度

2.启发式合并,根据定理7可以证明其与Treap以此法启发式合并的复杂度相同,即也可以做到$o(n\log n)$

3.对于分裂操作,需要将该节点Splay到根是$o(\log (n+m))$的,似乎无法支持

原题解:

很明显使用平衡树来维护,由于有单次操作和深度的限制,使用Treap比较方便

由于2和3类操作比较少,我们都可以以$o(\log n)$的复杂度去执行,下面考虑1类操作——

利用论文的Treap的快速合并,可以做到$o(\log \min(n,m))$的复杂度,但这样会带来一个问题,也就是最终合并后较小子树的根到较大子树的根这段路径上的节点不合法(即枯萎)

我们将这些节点打上标记,并归纳每一个树只有左链和右链可能有不合法节点,因此我们可以在$o(\log n)$的时间内使一个大小为$n$的二叉树合法,并在合并时使较小的一个二叉树合法即可进行归纳(分裂和查询直接对该二叉树修改即可)

由上,我们得到了一个合并$o(\log \min(n,m))$以及分裂和查询都是$o(\log n)$的操作,来分析复杂度——

定义一棵二叉树的势能为其大小的对数,定义一个状态的势能为其所有二叉树势能和

由于势能和不超过$n$,因此最初和最终状态势能差不超过$o(n)$,接下来考虑操作的均摊复杂度:

1.合并操作,假设合并两颗大小为$n$和$m$的二叉树,实际复杂度为$o(\log \min(n,m))$,而势能变化为$\log (n+m)-\log n-\log m$,均摊复杂度即两者之和为$\log (n+m)-\log \max(n,m)\le \log 2\sim o(1)$

2.分裂和查询操作,实际复杂度以及势能变化都是$o(\log n)$的,均摊复杂度即$o(\log n)$

当然,上面所述的复杂度级别是在每一次操作结束后都将两个指针最终都移动到最小值和最大值,但由于这样的移动仅仅可能会造成重复,因此不需要实际完成

综上,总复杂度为$o(n+m+k\log n)$,可以通过

(原题似乎已经过不了了,因为它的哈希没有写模数,在子树大小稍大时就会判定错误)

(代码的正确性应该是有的,但也仅供参考)

  1. 1 #include "tree.h"
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 #define N 200005
  5. 5 int S,rt[N<<2],a[N<<2][2],fa[N],sz[N],rnd[N],ch[N][2];
  6. 6 void up(int k){
  7. 7 sz[k]=sz[ch[k][0]]+sz[ch[k][1]]+1;
  8. 8 }
  9. 9 void up_root(int k){
  10. 10 while (k){
  11. 11 up(k);
  12. 12 k=fa[k];
  13. 13 }
  14. 14 }
  15. 15 void use_move(int k,int id,int x,int c,int y){
  16. 16 move(k,id+1,x,c,y);
  17. 17 a[k][id]=x;
  18. 18 if (c>=0){
  19. 19 assert(!fa[y]);
  20. 20 fa[ch[x][c]]=0;
  21. 21 if (y)fa[y]=x;
  22. 22 ch[x][c]=y;
  23. 23 }
  24. 24 up(x);
  25. 25 }
  26. 26 void init(int n,int maxdep,int maxcnt){
  27. 27 srand(time(0));
  28. 28 S=n;
  29. 29 for(int i=1;i<=n;i++){
  30. 30 fa[i]=0;
  31. 31 sz[i]=1;
  32. 32 rnd[i]=rand();
  33. 33 ch[i][0]=ch[i][1]=0;
  34. 34 rt[i]=a[i][0]=a[i][1]=i;
  35. 35 }
  36. 36 }
  37. 37 void get_min(int k){
  38. 38 while (ch[a[k][0]][0])use_move(k,0,ch[a[k][0]][0],-1,-1);
  39. 39 }
  40. 40 void get_max(int k){
  41. 41 while (ch[a[k][1]][1])use_move(k,1,ch[a[k][1]][1],-1,-1);
  42. 42 }
  43. 43 void clear(int k){
  44. 44 while (fa[a[k][0]])use_move(k,0,fa[a[k][0]],-1,-1);
  45. 45 while (fa[a[k][1]])use_move(k,1,fa[a[k][1]],-1,-1);
  46. 46 }
  47. 47 void join(int x,int y,int &id1,int &id2){
  48. 48 if (rnd[rt[x]]>rnd[rt[y]])clear(x);
  49. 49 else clear(y);
  50. 50 get_max(x),get_min(y);
  51. 51 while (1){
  52. 52 if (rnd[a[x][1]]<rnd[a[y][0]]){
  53. 53 while ((fa[a[y][0]])&&(rnd[a[x][1]]<rnd[fa[a[y][0]]]))use_move(y,0,fa[a[y][0]],-1,-1);
  54. 54 if (!fa[a[y][0]]){
  55. 55 use_move(x,1,a[x][1],1,a[y][0]);
  56. 56 up_root(a[x][1]);
  57. 57 id1=1,id2=2;
  58. 58 rt[++S]=rt[x];
  59. 59 memcpy(a[S],a[x],sizeof(a[x]));
  60. 60 break;
  61. 61 }
  62. 62 int k=a[y][0];
  63. 63 use_move(y,0,fa[a[y][0]],0,0);
  64. 64 use_move(x,1,a[x][1],1,k);
  65. 65 }
  66. 66 else{
  67. 67 while ((fa[a[x][1]])&&(rnd[fa[a[x][1]]]>rnd[a[y][0]]))use_move(x,1,fa[a[x][1]],-1,-1);
  68. 68 if (!fa[a[x][1]]){
  69. 69 use_move(y,0,a[y][0],0,a[x][1]);
  70. 70 up_root(a[y][0]);
  71. 71 id1=3,id2=4;
  72. 72 rt[++S]=rt[y];
  73. 73 memcpy(a[S],a[y],sizeof(a[y]));
  74. 74 break;
  75. 75 }
  76. 76 int k=a[x][1];
  77. 77 use_move(x,1,fa[a[x][1]],1,0);
  78. 78 use_move(y,0,a[y][0],0,k);
  79. 79 }
  80. 80 }
  81. 81 }
  82. 82 void split(int x,int y,int &p1,int &p2,int &p3,int &p4){
  83. 83 clear(x);
  84. 84 int k=rt[x];
  85. 85 while (k){
  86. 86 if (sz[ch[k][0]]<y){
  87. 87 if (rt[S+1]){
  88. 88 use_move(x,0,a[x][0],1,k);
  89. 89 use_move(x,0,k,-1,-1);
  90. 90 }
  91. 91 else{
  92. 92 rt[S+1]=k;
  93. 93 if (k!=rt[x]){
  94. 94 while (a[x][0]!=a[x][1])use_move(x,0,ch[a[x][0]][0],-1,-1);
  95. 95 use_move(x,0,a[x][0],0,k);
  96. 96 use_move(x,0,k,-1,-1);
  97. 97 use_move(x,1,a[x][1],0,0);
  98. 98 }
  99. 99 }
  100. 100 y-=sz[ch[k][0]]+1;
  101. 101 k=ch[k][1];
  102. 102 use_move(x,0,a[x][0],1,0);
  103. 103 }
  104. 104 else{
  105. 105 if (rt[S+2]){
  106. 106 use_move(x,1,a[x][1],0,k);
  107. 107 use_move(x,1,k,-1,-1);
  108. 108 }
  109. 109 else{
  110. 110 rt[S+2]=k;
  111. 111 if (k!=rt[x]){
  112. 112 while (a[x][1]!=a[x][0])use_move(x,1,ch[a[x][1]][1],-1,-1);
  113. 113 use_move(x,1,a[x][1],1,k);
  114. 114 use_move(x,1,k,-1,-1);
  115. 115 use_move(x,0,a[x][0],1,0);
  116. 116 }
  117. 117 }
  118. 118 k=ch[k][0];
  119. 119 use_move(x,1,a[x][1],0,0);
  120. 120 }
  121. 121 }
  122. 122 p1=rt[++S];
  123. 123 up_root(p2=a[x][0]);
  124. 124 a[S][0]=p1,a[S][1]=p2;
  125. 125 up_root(p3=a[x][1]);
  126. 126 p4=rt[++S];
  127. 127 a[S][0]=p3,a[S][1]=p4;
  128. 128 }
  129. 129 int visit(int x){
  130. 130 clear(x);
  131. 131 return rt[x];
  132. 132 }

[loj2506]tree的更多相关文章

  1. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  2. SAP CRM 树视图(TREE VIEW)

    树视图可以用于表示数据的层次. 例如:SAP CRM中的组织结构数据可以表示为树视图. 在SAP CRM Web UI的术语当中,没有像表视图(table view)或者表单视图(form view) ...

  3. 无限分级和tree结构数据增删改【提供Demo下载】

    无限分级 很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了. 说到无限分级,又要扯到递归调用了.(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据.当然,以 ...

  4. 2000条你应知的WPF小姿势 基础篇<45-50 Visual Tree&Logic Tree 附带两个小工具>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000Things You Should Know About C# 和 2,0 ...

  5. Leetcode 笔记 110 - Balanced Binary Tree

    题目链接:Balanced Binary Tree | LeetCode OJ Given a binary tree, determine if it is height-balanced. For ...

  6. Leetcode 笔记 100 - Same Tree

    题目链接:Same Tree | LeetCode OJ Given two binary trees, write a function to check if they are equal or ...

  7. Leetcode 笔记 99 - Recover Binary Search Tree

    题目链接:Recover Binary Search Tree | LeetCode OJ Two elements of a binary search tree (BST) are swapped ...

  8. Leetcode 笔记 98 - Validate Binary Search Tree

    题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...

  9. Leetcode 笔记 101 - Symmetric Tree

    题目链接:Symmetric Tree | LeetCode OJ Given a binary tree, check whether it is a mirror of itself (ie, s ...

随机推荐

  1. HTTP基础系列之:一文搞懂URL

    一般我们日常在上网的时候,会在浏览器的地址栏里输入一个网站的 "网址",点击下回车,就会跳到你想去的网站,就类似这样 但其实,叫做 "网址" 并不是特别的准确, ...

  2. L1-017 到底有多二 (15 分) java解题

    1 import java.util.Scanner; 2 3 public class Main { 4 public static void main(String args[]){ 5 doub ...

  3. jenkins容器内安装python3

    前言 很多小伙伴可能在考虑 jenkins 拉取了 github 上的代码后,发现还越少 python3 环境,那能怎么办呢? 咨询了一位运维朋友给我的答案是,将 python3 挂载到容器工作目录上 ...

  4. Netty 了解

    1.1 Netty 是什么? Netty is an asynchronous event-driven network application framework for rapid develop ...

  5. Bloom Filter算法

    Bloom Filter算法详解 什么是布隆过滤器 布隆过滤器(Bloom Filter)是 1970 年由布隆提出的.它实际上是一个很长的二进制向量和一系列随机映射函数 (下面详细说),实际上你也可 ...

  6. 【c++ Prime 学习笔记】第5章 语句

    C++提供了一组控制流语句,包括条件执行语句.循环语句.跳转语句. 5.1 简单语句 空语句 ; ,最简单的语句 别漏写分号,也别多写 while(cin>>s && s! ...

  7. 【数据结构与算法Python版学习笔记】图——最短路径问题、最小生成树

    最短路径问题 概念 可以通过"traceroute"命令来跟踪信息传送的路径: traceroute www.lib.pku.edu.cn 可以将互联网路由器体系表示为一个带权边的 ...

  8. 机器学习:SVM

    SVM 前言:支持向量机(Support Vector Machine, SVM),作为最富盛名的机器学习算法之一,其本身是一个二元分类算法,为了更好的了解SVM,首先需要一些前提知识,例如:梯度下降 ...

  9. [对对子队]测试报告Beta

    一.测试中发现的bug BETA阶段的新bug 描述 提出者(可能需要发现者在会议上复现) 处理人 是否解决 第四关中工作区的循环语句拖动到组件区后成本的大小比原来不一样的问题 梁河览 何瑞 是 循环 ...

  10. OO_JAVA_JML系列第三次作业__架构之谈

    OO_JAVA_JML系列第三次作业 ## ----架构之谈 目录 OO_JAVA_JML系列第三次作业 出发点 操作的可分离性 操作本身的多样性 实现手段:表驱动编程 储存 注册 出发点 操作的可分 ...