前言:

  1. 在这里十分十分感谢 \(\text{lxl}\) 和王思齐发明和总结了 \(\text{WBLT}\)。

  2. 因为网上关于 \(\text{WBLT}\) 的正确讲解(已除去那篇国家集训队论文,不过伪代码和图片部分的小细节错误还是不少的)非常的少(包括 OI-Wiki 上的 \(\text{Leafy Tree}\) 和 \(\text{WBLT}\) 的专题写的也全是假的。反正基本上只要用单旋的全是假的,不服的可以把代码给我,我教你卡),所以写下了本篇笔记来和大家一起交流。

  3. 因为本人尚菜,并没有完全掌握 \(\text{WBLT}\)(也就是说,并没有做完那几道经典的 \(\text{WBLT}\) 题目),不过后面会慢慢更新。

  4. 如若想要愉快的看完本篇笔记,建议学习线段树动态开点的思想(应该算 \(\text{trick}\) 吧)。

前置知识:

一、\(\text{Leafy Tree}\):

其实 \(\text{Leafy Tree}\) 是一类树,它的核心思想是将原始数据放在叶节点中,而非叶子节点维护区间内的信息。

听完核心思想,我想大家一定想到了一个我们喜闻乐见的数据结构——线段树。没错,线段树本质上也属于 \(\text{Leafy Tree}\),其特征非常明显,叶子结点维护的是原始信息,而非叶子节点你想要维护的区间信息。

其实,我认为基于 \(\text{Leafy Tree}\) 的 \(\text{BST}\) 实现,其实就是一种维护区间最大值的线段树,它赋予了线段树 \(\text{BST}\) 的性质,加以去除部分的无用节点,形成一棵十分自由的线段树。

如果各位对线段树十分熟悉(尤其是动态开点),下面内容将会给你一种十分熟悉的感觉。

下面将说基于 \(\text{Leafy Tree}\) 的 \(\text{BST}\) 实现。

二、基于 \(\text{Leafy Tree}\) 的 \(\text{BST}\) 实现:

首先,我们先梳理一下 \(\text{BST}\) 和 \(\text{Leafy Tree}\) 的性质:

\(\text{BST}\):

  1. 对于任意一棵 \(\text{BST}\) 来说,其根节点的左儿子一定不大于根节点,根节的右儿子一定不小于根节点。

  2. 插入一个节点时,其若小于等于根节点,则向左走,其若大于根节点,则向右走。

\(\text{Leafy Tree}\):

  1. 对于任意一棵 \(\text{Leafy Tree}\) 来说,其叶子结点一定是维护的原始数据,其非叶子节点一定是维护的区间信息。

  2. 无论何时,\(\text{Leafy Tree}\) 的一个节点要么有两个儿子,要么没有儿子。

此时,根据 \(\text{Leafy Tree}\) 的性质 \(1\),我们将所有的原始数据节点都放在叶子节点上,然后每两个节点上方加一个他们的父亲,维护的是这两个节点的最大值(注意,此时的两个节点不一定是两个原始数据节点)。

根据 \(\text{BST}\) 的性质 \(1\) 我们可以推出结论 \(1\):

左儿子一定小于等于右儿子。

根据这一结论,我们规定 \(\text{Leafy Tree}\) 上的每一个左儿子要小于等于右儿子。

然后一棵基于 \(\text{Leafy Tree}\) 的 \(\text{BST}\) 雏形就出来了,我们再来梳理一下:

  1. 任何一个非叶子节点维护的都是其所代表的区间的最大值。

  2. 任何一个左儿子都小于等于右儿子。

  3. 真实的数据都处于叶子节点中。

根据上述内容,我们就可以实现查找了,思路如下:

  • 如果当前要查找值小于等于当前节点的左儿子的值,则去左儿子中找,否则去右儿子找。

  • 如果当前节点是叶子节点,则判断是否相等,若不相等,则说明没有查找值,否则找到查找值。

Code:

  1. bool Find(PNODE T, int x){
  2. if(isLeaf(T))
  3. return T->value == x;
  4. if(x<=T->lchild->value)
  5. return Find(T->lchild,x);
  6. return Find(T->rchild,x);
  7. }

现在,我们来思考插入:

若我们有一个待插入的新节点 \(x\),则根据 \(\text{Leafy Tree}\) 的性质 \(1\),我们不难得出这个新节点一定是在叶子上的。综合一下,就不难得出插入的思路:

  • 如果当前待插入新节点 \(x\) 的值是小于等于当前节点左儿子的值,则进入当前节点左儿子,否则进入当前节点右儿子,然后更新当前节点(注意,一定要更新当前节点)。

  • 如果当前节点是一个叶子结点,则进行如下操作:

    • 建立两个新节点,一个的值等于当前节点的值,另一个的值等于新节点的值。

    • 比较两个节点的大小,将值较大的节点设为当前节点的右儿子,较小的设为左儿子(依据性质 \(2\))。

    • 更新当前节点。

Code:

  1. void Insert(PNODE T, int x){
  2. if(isLeaf(T)){
  3. T->lchild = newNode(x);
  4. T->rchild = newNode(x);
  5. if(T->lchild>T->rchild)
  6. swap(T->lchild,T->rchild);
  7. T->value = T->rchild->value;
  8. pushup(T);
  9. return ;
  10. }
  11. if(x<=T->lchild->value)
  12. Insert(T->lchild,x);
  13. else
  14. Insert(T->rchild,x);
  15. pushup(T);
  16. }

那么至于删除操作,则更加的暴力:

  • 若当前节点是叶子结点,则直接退出(因为此时我们的树为空)。

  • 若当前节点的左儿子的值小于等于待删除的节点的值,则进行下列操作:

    • 若左儿子是叶子节点,则若左儿子的值与待删除节点相同,进行下述操作:

      • 删除左儿子。

      • 将右儿子的值赋给当前节点。

      • 删除右儿子。

    • 否则,就走进左儿子。

  • 否则,对右儿子进行类似的操作。

Code:

  1. void Delete(PNODE T, int x){
  2. if(isLeaf(T))
  3. return;
  4. if(x<=T->lchild->value){
  5. if(isLead(T->lchild)){
  6. if(T->lchild->value!=x)
  7. return;
  8. deleteNode(T->lchild);
  9. PNODE buf = T->rchild;
  10. *T = *buf;
  11. deleteNode(buf);
  12. }
  13. else
  14. Delete(T->lchild,x);
  15. }
  16. else{
  17. if(isLeaf(T->rchild)){
  18. if(T->rchild->value!=x)
  19. return;
  20. deleteNode(T->rchild);
  21. PNODE buf = T->lchild;
  22. *T = *buf;
  23. deleteNode(buf);
  24. }
  25. else
  26. Delete(T->rchild,x);
  27. }
  28. pushup(T);
  29. }

三、\(\text{Weight Balanced Leafy Tree}\):

首先,我们引入概念 \(\text{Weight Balanced Tree}\)(权平衡树,又名 \(\text{BB}\;[\alpha]\) 树),其主要思想就是依照正常的方法插入删除节点,然后如果左右子树比例不满足平衡系数 \(\alpha\) 的话,则维护平衡。

听到这一点,大家肯定想到了知名的替罪羊树。确实,替罪羊树属于 \(\text{Weight Balanced Tree}\) 的一种,其维护方式暴力重构也是 \(\text{Weight Balanced Tree}\) 的一个维护平衡的方式之一。

然而 \(\text{Weight Balanced Tree}\) 只能暴力重构维护平衡吗?显然不是,除了重构,我们还可以像 \(\text{AVL}\) 树一样旋转来维护平衡,而 \(\text{Weight Balanced Leafy Tree}\) 维护平衡的方式就是旋转。

\(\text{Weight Balanced Leafy Tree}\) 的旋转分为单旋和双旋:

单旋长这样:

而双旋则是这样的:

注意:再次强调,一定要有双旋,不然平衡性是假的

然后源代码和不平衡的 \(\text{Leafy Tree}\) 差不多,就是给每个节点加上属性 \(size\),然后每次插入删除操作维护一下树的平衡并且上传的时候别忘了 \(size\) 的信息合并就可以了。

Code:

  1. void rotate(PNODE T,bool d){
  2. PNODE temp;
  3. if(!d){
  4. temp = T->rchild;
  5. T->rchild = T->lchild;
  6. T->lchild = T->rchild->lchild;
  7. T->rchild->lchild = T->rchild->rchild;
  8. T->rchild->rchild = temp;
  9. }
  10. else{
  11. temp = T->lchild;
  12. T->lchild = T->rchild;
  13. T->rchild = T->lchild->rchild;
  14. T->lchild->rchild = T->lchild->lchild;
  15. T->lchild->lchild = temp;
  16. }
  17. pushup(T->lchild);
  18. pushup(T->rchild);
  19. pushup(T);//注意,这里一定要先 pushup 儿子,再 pushup 父亲
  20. }

既然已经介绍完了维护平衡的方式,那么我们应当在什么时候维护平衡呢?

首先,我们设一个节点的平衡度为 \(\rho\),\(\rho\) 的定义如下:

\[\rho_x=\dfrac{weight_{x.left}}{weight_x}
\]

其中的 \(weight_x\) 表示子树 \(x\) 所包含的叶节点数量。

那么,我们认为一个节点是平衡的当且仅当 \(\rho\) 满足 \(\rho \ge \alpha\) 且 \(1-\rho \ge \alpha\),若当前节点不平衡,则若 \(\rho_{son} \ge \dfrac {1-2\alpha}{1-\alpha}\),则进行双旋,否则进行单旋。

那么为什么可以依照这个原则来维护呢?我们来尝试证明一下:

我们根据单旋的图示,设 \(\rho_x\) 为节点 \(X\) 的平衡度,\(\rho_y\) 表示节点 \(Y\) 的平衡度,\(\gamma_y\) 为单旋后节点 \(\boldsymbol{Y}\) 的平衡度。

根据图示和已知条件,我们可以得出:

\[0 < \rho_x < \alpha
\]
\[\alpha \le \rho_y \le 1-\alpha
\]

根据图示和单旋的定义,我们不难看出 \(\rho_x\)、\(\rho_y\)、\(\gamma_y\) 具有以下关系:

\[\gamma_y = \rho_x + \rho_y - \rho_x\rho_y
\]

我们已知 \(\rho_x\) 和 \(\gamma_y\) 就是当前子树旋转前和旋转后的平衡度,而我们旋转后子树想要达到平衡,则需要:

\[\alpha \le \gamma_y \le 1-\alpha
\]

我们此时可以将目标拆成两部分,分别是:

\[\begin{cases}
\gamma_y \ge \alpha \\
\gamma_y \le 1 - \alpha
\end{cases}
\]

此时,将 \(\rho_x\)、\(\rho_y\)、\(\gamma_y\) 的关系代入不等式组,易得:

\[\begin{cases}
\rho_x + \rho_y - \rho_x\rho_y \ge \alpha \\
\rho_x + \rho_y - \rho_x\rho_y \le 1 - \alpha
\end{cases}
\]

将式子稍微变形,即得:

\[\begin{cases}
\rho_y \ge \dfrac {\alpha - \rho_x}{1-\rho_x} \\\\
\rho_y \le \dfrac {1 - \alpha- \rho_x}{1-\rho_x}
\end{cases}
\]

此时,我们可以得出下列两个命题:

\[1.\;\forall \rho_y \in [\alpha, 1-\alpha]\;,\; \rho_x \in [0, \alpha ]\qquad \rho_y \ge \dfrac {\alpha - \rho_x}{1-\rho_x}
\]
\[2.\;\forall \rho_y \in [\alpha, 1-\alpha]\;,\; \rho_x \in [0, \alpha ]\qquad \rho_y \le \dfrac {1- \alpha - \rho_x}{1-\rho_x}
\]

我们此时的问题也变成了证明上述两个命题在什么情况下同时成立。

命题 \(1\) 在已知条件下是显然成立的,因为原命题等于:

\[\forall \rho_y \in [\alpha, 1-\alpha]\;,\; \rho_x \in [0, \alpha ]\qquad \rho_y \ge (\dfrac {\alpha - \rho_x}{1-\rho_x})_{\max}
\]

根据糖水不等式,易得:

\[(\dfrac {\alpha - \rho_x}{1-\rho_x})_{\max} = \alpha
\]

故此时,命题 \(1\) 显然成立。

那么,关于命题 \(2\),我们也可以有相似的证明过程:

\[\forall \rho_y \in [\alpha, 1-\alpha]\;,\; \rho_x \in [0, \alpha ]\qquad \rho_y \le (\dfrac {1 - \alpha - \rho_x}{1-\rho_x})_{\min}
\]

根据糖水不等式,易得:

\[\begin{array}{ll}
(\dfrac {1 - \alpha - \rho_x}{1 -\rho_x})_{\min} &= \dfrac{1-\alpha-\alpha}{1-\alpha}\\\\
&=\dfrac{1-2\alpha}{1-\alpha}
\end{array}
\]

代入原式,则得到:

\[\forall \rho_y \in [\alpha, 1-\alpha]\qquad \rho_y \le \dfrac{1-2\alpha}{1-\alpha}
\]

此时我们可以看出,当 \(\rho_y \in (\dfrac{1-2\alpha}{1-\alpha},1-\alpha]\) 的时候,命题二不成立,故我们需要对 \(\rho_y\) 的范围进行收缩到 \(\rho_y \in [\alpha, \dfrac{1-2\alpha}{1-\alpha}]\) 上述两个命题才同时成立。

综上,若单旋能维持平衡性,则需要 \(\rho_y \le \dfrac{1-2\alpha}{1-\alpha}\),否则则必须进行双旋。

证毕。

所以此时的维护应当这么写:

  • 若当前节点左儿子的大小当前节点的大小的比值小于 \(\alpha\),则:

    • 若当前右儿子的左儿子的大小当前右儿子的大小的比值大于等于 \(\dfrac{1-2\alpha}{1-\alpha}\),则进行双旋。

    • 否则进行单旋。

  • 否则若当前节点右儿子的大小当前节点的大小的比值小于 \(\alpha\),则:

    • 若当前左儿子的右儿子的大小当前左儿子的大小的比值大于等于 \(\dfrac{1-2\alpha}{1-\alpha}\),则进行双旋。

    • 否则进行单旋。

Code:

  1. void maintain(PNODE T){
  2. if(!isLeaf(T)){
  3. if(T->lchild->size < T->size * alpha)
  4. d = 1;
  5. else if(T->rchild->size < T->size * alpha)
  6. d = 0;
  7. else
  8. return;
  9. if(d){
  10. if(T->rchild->lchild->size >= T->rchild->size * (1-2*alpha)/(1-alpha))
  11. rotate(T->rchild,!d);
  12. }
  13. else
  14. if(T->lchild->rchild->size >= T->lchild->size * (1-2*alpha)/(1-alpha))
  15. rotate(T->lchild,!d);
  16. rotate(T,d);
  17. pushup(T);
  18. }
  19. }

关于平衡因子 \(\alpha\) 的取值问题,实际上只要取在区间 \([0,\dfrac{3-\sqrt{5}}{2}]\) 之间的话都可以,证明太显然了,在这里受篇幅限制就不展开了,证明思路就考虑区间的定义即可,具体参见高一集合部分。个人实践得出 \(\alpha\) 最佳取值是 \(0.2928\),不过具体而言则是因题而异。

附普通平衡树板子:

\(\text{Code}\)

四、\(\text{Weight Balanced Leafy Tree}\) 的区间操作:

既然 \(\text{WBLT}\) 和线段树这么像,那么我们是不是可以按照线段树的方法对其进行区间操作呢?答案是可以的,但是仅针对于线段树所能维护的操作这样打标记。

例如,我们利用 \(\text{WBLT}\) 过掉线段树板板题的时候就可以这么干,不过碍于篇幅限制,我就只好将代码扔到云剪贴板里了,有兴趣的可以去看看。

不过,如果遇到线段树维护不了的操作,我们的 \(\text{WBLT}\) 就束手无策……了吗?

其实并没有,因为 \(\text{WBLT}\) 可以分裂和合并的特性(实际上这个特性是从线段树继承过来的),加之平衡树的高度自由性,我们不难想到可以用类似 \(\text{FHQ Treap}\) 的方法来进行维护一些线段树特性所不支持的操作(因为他毕竟不能像 \(\text{Splay}\) 那样把区间旋上去)。

不过,\(\text{WBLT}\) 的 \(\operatorname{Split}\) 和 \(\operatorname{Merge}\) 操作并不像 \(\text{FHQ Treap}\) 那么好写(并且常数还大了),因为 \(\operatorname{Merge}\) 和 \(\operatorname{Split}\) 操作有可能会让原先的 \(\text{WBLT}\) 失去其自身的性质,这可能也是 \(\text{WBLT}\) 为数不多的痛点吧(不过也有可能自己写丑了)。

现在,我们考虑合并操作,对于给定的两棵树 \(A\) 和 \(B\):

  • 若 \(size_B \ge size_A \cdot \alpha\) 则直接将 \(A\)、\(B\) 合并,即将 \(A\) 设为右儿子,\(B\) 设为左儿子。

  • 否则,进行下述操作:

    • 若 \(size_A \ge size_B\):

      • 若将 \(B\) 直接合并到 \(A\) 的右儿子能保持平衡,那就合并,然后其结果再与 \(A\) 的左儿子合并。

      • 否则,将 \(A\) 的左儿子的右儿子与 \(A\) 的右儿子进行合并,将 \(A\) 的左儿子的左儿子与 \(B\) 进行合并,再将两者的合并结果进行合并。

    • 否则:

      • 若将 \(A\) 直接合并到 \(B\) 的左儿子能保持平衡,那就合并,然后其结果再与 \(B\) 的右儿子合并。

      • 否则,将 \(A\) 与 \(B\) 的左儿子的左儿子合并,将 \(B\) 的左儿子的右儿子与 \(B\) 的右儿子进行合并,再将两者的结果进行合并。

实际上,\(\operatorname{Merge}\) 的函数其实本质上就是:若两者直接合并平衡,则直接合并,否则单旋后合并,否则双旋后合并。

Code:

  1. inline PNODE Merge(PNODE a,PNODE b) //打大写太麻烦了,只好用小写了/kel
  2. {
  3. if(a==nullptr)
  4. return b;
  5. if(b==nullptr)
  6. return a;
  7. if(b->size >= a->size * alpha / (1-alpha))//能直接合并则直接合并
  8. return mix(a,b);
  9. PNODE ret = nullptr;
  10. if(a->size > b->size){
  11. pushdown(a);
  12. if(a->lchild->size >= (a->size + b->size) * alpha){
  13. ret = Merge(a->lchild, Merge(a->rchild,b));
  14. deleteNode(a);//此时原节点 a 变成了垃圾节点,要回收空间
  15. return ret;
  16. }
  17. pushdown(a->rchild);
  18. ret = Merge(Merge(a->lchild, a->rchild->lchild), Merge(a->rchild->rchild, b));
  19. deleteNode(a);//此时原节点 a 变成了垃圾节点,要回收空间
  20. return ret;
  21. }
  22. else{
  23. pushdown(b);
  24. if(b->rchild->size >= (a->size + b->size) * alpha){
  25. ret = Merge(Merge(a, b->lchild), b->rchild);
  26. deleteNode(b);//此时原节点 b 变成了垃圾节点,要回收空间
  27. return ret;
  28. }
  29. pushdown(b->lchild);
  30. ret = Merge(Merge(a, b->lchild->lchild), Merge(b->lchild->rchild, b->rchild));
  31. deleteNode(b);//此时原节点 b 变成了垃圾节点,要回收空间
  32. return ret;
  33. }
  34. }

注意:\(\boldsymbol{\operatorname{Merge}}\) 操作不满足交换律。

有了 \(\operatorname{Merge}\) 了以后,\(\operatorname{Split}\) 就好写多了(实际上也没好写到哪里去)。

\(\text{WBLT}\) 的 \(\operatorname{Split}\) 实际上也可以分为两种,分别是“按秩分裂”和“按值分裂”。

我们先谈谈按秩分裂。

我们思考一下,若当前还要分裂的大小为 \(0\) 了,那么我们还需要分裂吗?显然不需要了。故此时我们就可以将分裂出来的树 \(X\) 置为空。

那么如果当前要分裂的树是一个叶子节点,那么我们显然也不需要分类了,直接将树 \(X\) 置为当前树 \(T\) 就行了。

否则的话,我们首先将标记下传,然后如果当前待分裂的大小小于等于我们左子树的大小,则先将左子树分裂成两部分,一部分是我们需要的,另一部分是分剩下的。

此时 Merge 就派上用场了,将剩下的和树 \(T\) 的左子树进行合并,就是我们需要的另一部分树 \(Y\)。

对于右子树,我们进行类似的操作,只不过在分裂右子树时所用的大小应当是原大小减去左子树大小。

Code:

  1. // 先行注意,传参之前请先将子树 X 和 Y 置为 nullptr。
  2. void Split(PNODE T, int x, PNODE &X, PNODE &Y){
  3. if(!x){
  4. Y = T;
  5. return;
  6. }
  7. if(isLeaf(T)){
  8. X = T;
  9. return ;
  10. }
  11. pushdown(T);
  12. if(x<=X->lchild->size){
  13. PNODE bufY = nullptr;
  14. Split(T->lchild, x, X, bufY);
  15. Y = Merge(bufY,T->rchild);
  16. } else {
  17. PNODE bufX = nullptr;
  18. Split(T->rchild, x - T->lchild->size, bufX, Y);
  19. X = Merge(T->lchild, bufX);
  20. }
  21. }

至于按值分裂,实际上和按秩分裂是一模一样的,只不过按值分裂不需要判断 \(x\) 是否为 \(0\),且分裂右子树的时候不需要将 \(x\) 减去左子树的大小,剩下就基本上一模一样了。碍于篇幅限制,我们就不进行进一步的代码演示了。

注意:若 \(\operatorname{Merge}\) 和 \(\operatorname{Split}\) 执行次数过多,则会产生无法接受的垃圾节点数量,故我们在写 \(\operatorname{Merge}\) 和 \(\operatorname{Split}\) 的时候,请务必做好垃圾回收!!

附文艺平衡树代码:

\(\text{Code}\)

五、\(\text{Weight Balanced Leafy Tree}\) 实现可并堆:

在讲 \(\text{WBLT}\) 的可持久化之前,我们先讲点整活的部分。

目前好像是没有找到哪一篇文章写 \(\text{WBLT}\) 怎么实现可并堆,不过可能是因为其思路过于简单或者是左偏树实在是太好写了。

按照惯例,我们先来回顾一下堆的性质:

  1. 根节点大于等于或小于等于其所有的儿子节点

  2. 堆是一棵完全二叉树。

然后,我们把目光放到所有 \(\text{WBLT}\) 节点上,尤其是非叶子节点上,就可以看到一些平时在平衡树上所看不到的东西:

父节点维护的是其表示的区间的最大值。

诶,等等,这不就意味着父节点是它所代表的区间内的值最大的节点吗?确实,所以我们此时不难看出,\(\text{WBLT}\) 的本身也是满足堆的性质 \(1\) 的。

至于性质 \(2\),兄弟,这是平衡树啊!!所以 \(\text{WBLT}\) 是可以当堆使的。

又因为 \(\text{WBLT}\) 支持合并,所以自然而然的就可以做可并堆来使了。

碍于篇幅限制,我将过掉可并堆模板题的代码放到云剪贴板里面了,有兴趣的大家可以去康一康,顺便看看有没有什么数据能把它卡掉,这里就不多赘述了。

Weight Balanced Leafy Tree 学习笔记的更多相关文章

  1. 珂朵莉树(Chtholly Tree)学习笔记

    珂朵莉树(Chtholly Tree)学习笔记 珂朵莉树原理 其原理在于运用一颗树(set,treap,splay......)其中要求所有元素有序,并且支持基本的操作(删除,添加,查找......) ...

  2. dsu on tree学习笔记

    前言 一次模拟赛的\(T3\):传送门 只会\(O(n^2)\)的我就\(gg\)了,并且对于题解提供的\(\text{dsu on tree}\)的做法一脸懵逼. 看网上的其他大佬写的笔记,我自己画 ...

  3. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  4. 矩阵树定理(Matrix Tree)学习笔记

    如果不谈证明,稍微有点线代基础的人都可以在两分钟内学完所有相关内容.. 行列式随便找本线代书看一下基本性质就好了. 学习资源: https://www.cnblogs.com/candy99/p/64 ...

  5. k-d tree 学习笔记

    以下是一些奇怪的链接有兴趣的可以看看: https://blog.sengxian.com/algorithms/k-dimensional-tree http://zgjkt.blog.uoj.ac ...

  6. splay tree 学习笔记

    首先感谢litble的精彩讲解,原文博客: litble的小天地 在学完二叉平衡树后,发现这是只是一个不稳定的垃圾玩意,真正实用的应有Treap.AVL.Splay这样的查找树.于是最近刚学了学了点S ...

  7. LSM Tree 学习笔记——本质是将随机的写放在内存里形成有序的小memtable,然后定期合并成大的table flush到磁盘

    The Sorted String Table (SSTable) is one of the most popular outputs for storing, processing, and ex ...

  8. LSM Tree 学习笔记——MemTable通常用 SkipList 来实现

    最近发现很多数据库都使用了 LSM Tree 的存储模型,包括 LevelDB,HBase,Google BigTable,Cassandra,InfluxDB 等.之前还没有留意这么设计的原因,最近 ...

  9. Expression Tree 学习笔记(一)

    大家可能都知道Expression Tree是.NET 3.5引入的新增功能.不少朋友们已经听说过这一特性,但还没来得及了解.看看博客园里的老赵等诸多牛人,将Expression Tree玩得眼花缭乱 ...

  10. K-D Tree学习笔记

    用途 做各种二维三维四维偏序等等. 代替空间巨大的树套树. 数据较弱的时候水分. 思想 我们发现平衡树这种东西功能强大,然而只能做一维上的询问修改,显得美中不足. 于是我们尝试用平衡树的这种二叉树结构 ...

随机推荐

  1. study the docker network of macvlan

    Introduce: 在 Macvlan 出现之前,我们只能为一块以太网卡添加多个 IP 地址,却不能添加多个 MAC 地址,因为 MAC 地址正是通过其全球唯一性来标识一块以太网卡的,即便你使用了创 ...

  2. chrome pre 自动换行

    问题引出 当我想要使用chrome的打印功能生成一份关于md的pdf版本的时候发现有的代码块没有自动换行,生成的PDF没有自动换行,导致部分信息无法阅读 处理方式 把有自动换行的部分处理一下,在md文 ...

  3. go项目实现在配置文件实现配置项统一管理

    转载请注明出处: go项目中实现配置项统一管理,实现逻辑:将 配置项整理为一个json的数据结构,并保存到go.conf文件中,然后在go项目启动main方法中加载 go.conf 文件,读取go.c ...

  4. 加密解决HTTP协议带来的安全问题

    HTTP协议默认是采取明文传输的,容易被中间人窃听.拦截.篡改,存在安全隐患. 常见提高安全性的方法是对通信内容进行加密,再进行传输,常见的加密方式有 不可逆加密:单向散列函数 可逆加密:对称加密.非 ...

  5. QA|如何实现一个函数读取1-9按钮?|网页计算器自动化测试实战

    如何实现一个函数读取1-9按钮? 如何实现一个根据我们Json存的数字,自动选取对应按钮点击,并且点击对应算法? 首先我们肯定不能给每个元素都写一个函数吧,毕竟挺多,而且如果这样写了,那加减乘除都要分 ...

  6. 重磅| Falcon 180B 正式在 Hugging Face Hub 上发布!

    引言 我们很高兴地宣布由 Technology Innovation Institute (TII) 训练的开源大模型 Falcon 180B 登陆 Hugging Face! Falcon 180B ...

  7. 一键安装lnmp 环境

    一键安装lnmp 环境 目录 一键安装lnmp 环境 操作步骤 1.添加网站(虚拟主机) 2.伪静态管理 3.上传网站程序 4.已存在虚拟主机添加ssl证书开启https 5.列出网站(虚拟主机) 6 ...

  8. 2.12 PE结构:实现PE字节注入

    本章笔者将介绍一种通过Metasploit生成ShellCode并将其注入到特定PE文件内的Shell注入技术.该技术能够劫持原始PE文件的入口地址,在PE程序运行之前执行ShellCode反弹,执行 ...

  9. Go语句与表达式深度解析:全案例手册

    关注公众号[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作者拥有10+年互联网服务架构.AI产品研发经验.团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资 ...

  10. 【krpano】多分类缩略图及多分类地图案例

    该案例提供了场景多分类缩略图展示以及多地图展示,效果如下截图:                 下载地址:http://pan.baidu.com/s/1hsA5ta8 感谢群内小伙伴H·T·T的分享 ...