jemalloc 深入分析 之red-black tree 红黑树

为了更好的阅读效果,推荐下载pdf文档:
详细文章请参考:《jemalloc 深入分析》
https://github.com/everschen/tools/blob/master/DOC/Jemalloc.pdf
https://download.csdn.net/download/ip5108/10941278

2.4. red-black tree 红黑树

  • cpp macro implementation of left-leaning 2-3 red-black trees. Parent
  • pointers are not used, and color bits are stored in the least significant
  • bit of right-child pointers (if RB_COMPACT is defined), thus making node
  • linkage as compact as is possible for red-black trees.
    在AVL树中任何节点的两个儿子子树的高度最大差别为一,查找、插入和删除在平均和最坏情况下都是O(lg n)。但因为增加和删除节点可能会破坏“平衡状态”,所以大多数情况下需要通过多次树旋转来重新平衡这个树。所以简单地说,如果你的应用查找次数远远多于增删操作,那么AVL是最好的,但是如果增删次数和查找次数不相上下时,RBT因为相比AVL没有过多的旋转操作,效率要比AVL高。并且在是实际情况中,RBT的应用也更为广泛。

2.4.1. AVL 失去平衡后的4种姿态 如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图: 上图中的4棵树都是"失去平衡的AVL树",从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图: 上面的两张图都是为了便于理解,而列举的关于"失去平衡的AVL树"的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都由各自的定义: (1) LL:LeftLeft,也称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。 例如,在上面LL情况中,由于"根节点(8)的左子树(4)的左子树(2)还有非空子节点",
而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)“高2。 (2) LR:LeftRight,也称为"左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。 例如,在上面LR情况中,由于"根节点(8)的左子树(4)的左子树(6)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)“高2。 (3) RL:RightLeft,称为"右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。 例如,在上面RL情况中,由于"根节点(8)的右子树(12)的左子树(10)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)“高2。 (4) RR:RightRight,称为"右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。 例如,在上面RR情况中,由于"根节点(8)的右子树(12)的右子树(14)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。 前面说过,如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。AVL失去平衡之后,可以通过旋转使其恢复平衡,下面分别介绍"LL(左左),LR(左右),RR(右右)和RL(右左)"这4种情况对应的旋转方法。

2.4.2. AVL 旋转 LL的旋转 LL失去平衡的情况,可以通过一次旋转让AVL树恢复平衡。如下图:

图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。 对于LL旋转,你可以这样理解为:LL旋转是围绕"失去平衡的AVL根节点"进行的,也就是节点k2;而且由于是LL情况,即左左情况,就用手抓着"左孩子,即k1"使劲摇。将k1变成根节点,k2变成k1的右子树,“k1的右子树"变成"k2的左子树”。 RR的旋转 理解了LL之后,RR就相当容易理解了。RR是与LL对称的情况!RR恢复平衡的旋转方法如下: 图中左边是旋转之前的树,右边是旋转之后的树。RR旋转也只需要一次即可完成。 LR的旋转 LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡。如下图:

第一次旋转是围绕"k1"进行的"RR旋转",第二次是围绕"k3"进行的"LL旋转"。 RL的旋转 RL是与LR的对称情况!RL恢复平衡的旋转方法如下: 第一次旋转是围绕"k3"进行的"LL旋转",第二次是围绕"k1"进行的"RR旋转"。

2.4.3. RB TREE定义
和AVL树一样,红黑树也是一种自平衡二叉排序树,其定义如下:
(1)节点有且只有两种颜色,红色和黑色。
(2)根节点和叶子节点必须是黑色,其中,叶子节点是虚拟存在的空节点(NULL)。
(3)红色节点的两个子节点必须是黑色。
(4)任意节点到叶子节点的路径上,必须包含相同数目的黑色节点。
从红黑树的定义可以发现,任意节点左右子树的高度差在一倍之内(最长路径为节点红黑相间,最短路径为节点全黑)。
由于红黑树对平衡性的要求没有AVL树高,因此频繁插入和删除节点时,触发平衡调整的次数更少,平衡调整的过程也更易收敛。

2.4.4. RB TREE 插入过程
新插入节点为红色,当往上回溯,如果碰到一个黑色的父辈节点,则完成。
a_attr void
a_prefix##insert(a_rbt_type *rbtree, a_type *node) {
struct {
a_type *node;
int cmp;
} path[sizeof(void *) << 4], pathp;
rbt_node_new(a_type, a_field, rbtree, node);
/
Wind. /
path->node = rbtree->rbt_root; \ //这里把插入点的路径节点放入path数组,类似于一个栈的数组,第一个是树的根节点,最后一个是需要插入的node自己,并且记录当前节点和node节点大小比较值,放入cmp中,为后续使用做准备。
for (pathp = path; pathp->node != NULL; pathp++) {
int cmp = pathp->cmp = a_cmp(node, pathp->node);
assert(cmp != 0);
if (cmp < 0) {
pathp[1].node = rbtn_left_get(a_type, a_field,
pathp->node);
} else {
pathp[1].node = rbtn_right_get(a_type, a_field,
pathp->node);
}
}
pathp->node = node;
/
Unwind. */ \ //放入完成后,从最后面开始,处理放入路径的节点,每次取栈顶两个节点进行处理。
for (pathp–; (uintptr_t)pathp >= (uintptr_t)path; pathp–) { \ a_type *cnode = pathp->node;//当前栈顶第二个节点
if (pathp->cmp < 0) { \ a_type *left = pathp[1].node;//路径经过左侧,当前栈顶第一个节点 \ rbtn_left_set(a_type, a_field, cnode, left);//重新连接,可能有新的子树根节点生成 \ if (rbtn_red_get(a_type, a_field, left)) { //如果当前top/left为红
a_type leftleft = rbtn_left_get(a_type, a_field, left);
if (leftleft != NULL && rbtn_red_get(a_type, a_field,
leftleft)) {
/
Fix up 4-node. */
a_type *tnode; \ rbtn_black_set(a_type, a_field, leftleft);//置leftleft为黑 \

rbtn_rotate_right(a_type, a_field, cnode, tnode);//右旋 \ cnode = tnode;//把新的子树root,即left/top作为下一轮栈顶 \ //这里调整解决的是leftleft和left都是红色的问题,把leftleft置为黑色,然后把left右旋,这样left成为了新的子树的根,也就是新的下一次待处理的栈顶。处理前cnode到leftleft有一个黑色节点,它自己,处理后leftleft变为了黑色的节点,cnode跑到新子树的右边,所以对比新子树和原子树,黑色节点个数没有变化,同时调整了两个红色节点相连的问题。 变化如下图所示:
|
cnode(b)
/
left® a
/
leftleft®
|
left®
/
leftleft(b) cnode(b)

a
}
} else { \ return; //如果栈顶为黑色,结束返回
}
} else { \ a_type *right = pathp[1].node; //路径经过右侧,当前栈顶第一个节点 \ rbtn_right_set(a_type, a_field, cnode, right); //重新连接,可能有新的子树根节点生成 \ if (rbtn_red_get(a_type, a_field, right)) {//如果当前top/right为红
a_type left = rbtn_left_get(a_type, a_field, cnode);
if (left != NULL && rbtn_red_get(a_type, a_field,
left)) {
/
Split 4-node. / \ //把左右两个节点都置黑色,父节点置红色
rbtn_black_set(a_type, a_field, left);
rbtn_black_set(a_type, a_field, right);
rbtn_red_set(a_type, a_field, cnode); \ //该算法的终结条件是栈顶元素为黑色,这个分支是当前栈顶是红色,它的左兄弟节点也是红色,这里直接把两个兄弟节点置黑色,把cnode置红色,继续处理下一个栈元素。
} else {
/
Lean left. */
a_type tnode; \ bool tred = rbtn_red_get(a_type, a_field, cnode);//保持当前节点颜色\ rbtn_rotate_left(a_type, a_field, cnode, tnode);//左旋 \ rbtn_color_set(a_type, a_field, tnode, tred);//置新的根节点颜色为原根节点cnode的颜色 \ rbtn_red_set(a_type, a_field, cnode);//cnode作为左孩子,置为红 \ cnode = tnode;//用新的根节点作为下一次的栈顶节点 \ //这个分支是当前栈顶红色,它的左兄弟不存在或者为黑色,把cnode左旋,栈顶元素成为新的子树根节点,所以最后需要设cnode=tnode,把新的子树根节点置为原来的conde的颜色,conde置为红色,也就是原来栈顶的颜色。这样处理可能为下一次的调整做准备,同时又往根节点方向推进了一步。
}
} else { \ return; //如果栈顶为黑色,结束返回
}
} \ pathp->node = cnode;//保存新的栈顶
} \ /
Set root, and make it black. */
rbtree->rbt_root = path->node;
rbtn_black_set(a_type, a_field, rbtree->rbt_root);
}
根据当前插入节点在父节点的左侧或者右侧分为两种情况讨论:
插入的红色节点在左边时:
1) 如果父节点为黑色,直接完成;
2) 如果父节点为红色,在下一轮循环时就出现了左和左左都是红色的情况,这个时候左左变黑,左成为新的根节点,左的父节点一定是黑,要不然原来的子树不符合rb树规则。调整后左变为新的根节点,还是红色,左左变为新的左,为黑色。原
Jemalloc 深入分析
Copyright 2013 Spreadtrum Communications Inc. 32
来的黑色的根节点成为新的右节点,黑色。因为新的根节点变为了红色,所以继续向上回溯,直到找到黑色的节点为止。
插入的红色节点在右边时:
3) 如果父节点的左孩子不存在或者为黑色,且父节点为黑色,左旋,使得新节点成为新的父节点,设为原父节点的颜色黑色。原来的父节点成为左孩子,设为红色,因为新的父节点为黑色,下一轮循环时就会退出。
4) 如果父节点的左孩子不存在或者为黑色,且父节点为红色,左旋,使得新节点成为新的父节点,设为原父节点的颜色红色。原来的父节点成为左孩子,设为红色,进入下一个循环,下一个循环的时候就会出现左是红色,左左也是红色的情况,和2)的下一轮循环一样处理。
5) 如果父节点的左孩子存在,且为红色,这个时候设置左右孩子为黑色,父节点为红色,因为父节点颜色发生了变化,所以需要继续往根回溯,直到遇到黑色节点为止。(这里需要继续回溯的原因是,担心因为父节点变为红色后,出现红-红的情况。)
以下是key为3,1,5,4,2的插入过程的例子:
在这里插入图片描述
2.4.5. RB TREE删除过程
删除过程和插入相反,如果删除的是红色节点,因为不影响平衡,所以直接完成。如果是黑色节点,那么必须回溯恢复平衡。如果子树平衡恢复,可以直接终止回溯,如果子树还是小一个黑色节点,需要继续向上回溯。
a_attr void
a_prefix##remove(a_rbt_type *rbtree, a_type *node) {
struct {
a_type *node;
int cmp;
} *pathp, *nodep, path[sizeof(void ) << 4];
/
Wind. /
nodep = NULL; /
Silence compiler warning. /
path->node = rbtree->rbt_root;
for (pathp = path; pathp->node != NULL; pathp++) {
int cmp = pathp->cmp = a_cmp(node, pathp->node);
if (cmp < 0) {
pathp[1].node = rbtn_left_get(a_type, a_field,
pathp->node);
} else {
pathp[1].node = rbtn_right_get(a_type, a_field,
pathp->node);
if (cmp == 0) {
/
Find node’s successor, in preparation for swap. / \ //把node节点的后继保存起来,为后续处理做准备
pathp->cmp = 1;
nodep = pathp;
for (pathp++; pathp->node != NULL;
pathp++) {
pathp->cmp = -1;
pathp[1].node = rbtn_left_get(a_type, a_field,
pathp->node);
}
break;
}
}
Jemalloc 深入分析
Copyright 2013 Spreadtrum Communications Inc. 35
}
assert(nodep->node == node);
pathp–;
if (pathp->node != node) {
/
Swap node with its successor. / \ //用最后一个最靠近的node的后继节点代替node //保存替换节点的颜色
bool tred = rbtn_red_get(a_type, a_field, pathp->node); \ //把替换节点着色为要删除节点的颜色
rbtn_color_set(a_type, a_field, pathp->node,
rbtn_red_get(a_type, a_field, node)); \ //设置替换节点的左孩子为删除节点的左孩子 rbtn_left_set(a_type, a_field, pathp->node,
rbtn_left_get(a_type, a_field, node));
/
If node’s successor is its right child, the following code /
/
will do the wrong thing for the right child pointer. /
/
However, it doesn’t matter, because the pointer will be /
/
properly set when the successor is pruned. /\ //设置替换节点的右孩子为删除节点的右孩子,如果替换节点就是删除节点的右孩子,这会造成替换节点的右孩子其实是指向自己,等后续删除节点删除后不会有影响。 rbtn_right_set(a_type, a_field, pathp->node,
rbtn_right_get(a_type, a_field, node)); \ //把删除节点设置为替换节点的颜色 rbtn_color_set(a_type, a_field, node, tred);
/
The pruned leaf node’s child pointers are never accessed /
/
again, so don’t bother setting them to nil. */\ //交换path栈的替换节点和删除节点
nodep->node = pathp->node;
pathp->node = node; \ //如果删除节点是根节点,需要置新的根节点
if (nodep == path) {
rbtree->rbt_root = nodep->node;
} else { \ //设置父节点到替换节点(原来的删除节点被替换了)的链接
if (nodep[-1].cmp < 0) {
rbtn_left_set(a_type, a_field, nodep[-1].node,
nodep->node);
} else {
rbtn_right_set(a_type, a_field, nodep[-1].node,
nodep->node);
}
}
} else { \ //如果删除节点就是叶子节点
a_type left = rbtn_left_get(a_type, a_field, node);
if (left != NULL) {
/
node has no successor, but it has a left child. /
/
Splice node out, without losing the left child. /\ //没有右孩子,所以一定是黑色的
assert(!rbtn_red_get(a_type, a_field, node)); \ //没有右孩子,如果left也是黑色,那么node节点左右子树的黑色节点数不相等,所以left一定红色的。
assert(rbtn_red_get(a_type, a_field, left)); \ //直接把左孩子者黑色,用左孩子替换删除节点
rbtn_black_set(a_type, a_field, left);
if (pathp == path) {
rbtree->rbt_root = left;
} else { \ //设置父节点到替换节点(原来的删除节点被替换了)的链接
if (pathp[-1].cmp < 0) {
rbtn_left_set(a_type, a_field, pathp[-1].node,
left);
} else {
rbtn_right_set(a_type, a_field, pathp[-1].node,
left);
}
}
return;
} else if (pathp == path) { \ //如果删除节点是根节点,而且这个节点没有后继,左孩子也没有,所以是唯一的节点。
/
The tree only contained one node. /
rbtree->rbt_root = NULL;
return;
}
}
if (rbtn_red_get(a_type, a_field, pathp->node)) {
/
Prune red node, which requires no fixup. / \ //这里pathp->node就是需要删除的节点,但是他的颜色已经替换为替换它的节点的颜色,他在RB Tree里的位置也被移除,在path栈的位置被替换到了最顶端。 //这里assert是指没有右节点为红色的RB树,只有左节点为红色的RB树,如果右节点为红色,插入的时候如果左节点为红色,则需要同时变为黑色;如果左节点为黑色或者为空,则需要左旋,所以这里确保红色的节点只出现在左孩子这边。所以它的父节点的cmp<0。
assert(pathp[-1].cmp < 0);
rbtn_left_set(a_type, a_field, pathp[-1].node, NULL);
return;
}
/
The node to be pruned is black, so unwind until balance is /
/
restored. */\ //开始回溯,先把栈顶节点置空,表示后续子树为空,为回溯做准备。
pathp->node = NULL;
for (pathp–; (uintptr_t)pathp >= (uintptr_t)path; pathp–) {
assert(pathp->cmp != 0);
if (pathp->cmp < 0) { \ //删除黑色节点子树出现在左子树时 //重置左子树,左子树根节点可能有调整。
rbtn_left_set(a_type, a_field, pathp->node,
pathp[1].node);
if (rbtn_red_get(a_type, a_field, pathp->node)) { \ //如果当前节点为红色时
a_type *right = rbtn_right_get(a_type, a_field,
pathp->node);
a_type *rightleft = rbtn_left_get(a_type, a_field,
right);
a_type tnode;
if (rightleft != NULL && rbtn_red_get(a_type, a_field,
rightleft)) { \ //如果当前节点为红色时,right应该是黑色,如果rightleft也是红色时
/
In the following diagrams, ||, //, and \ /
/
indicate the path to the removed node. /
/
/
/
|| /
/
pathp® /
/
// \ /
/
(b) (b) /
/
/ /
/
® /
/
/\ //通过右旋和左旋,使得新的左子树黑色节点增加一,右子树黑色节点保持不变,达到平衡。
rbtn_black_set(a_type, a_field, pathp->node);
rbtn_rotate_right(a_type, a_field, right, tnode);
rbtn_right_set(a_type, a_field, pathp->node, tnode);
rbtn_rotate_left(a_type, a_field, pathp->node,
tnode);
} else { \ //如果当前节点为红色时,right应该是黑色,如果rightleft是黑色时
/
|| /
/
pathp® /
/
// \ /
/
(b) (b) /
/
/ /
/
(b) /
/
/\ //直接左旋,使得新子树的左子树黑色节点数增加一,新子树的右子树黑色节点数保持不变。达到平衡。新子树的根节点发生变化。
rbtn_rotate_left(a_type, a_field, pathp->node,
tnode);
}
/
Balance restored, but rotation modified subtree /
/
root. */\ //因为当前节点为红色,所以肯定不是树的根节点
assert((uintptr_t)pathp > (uintptr_t)path);
if (pathp[-1].cmp < 0) {
Jemalloc 深入分析
Copyright 2013 Spreadtrum Communications Inc. 39
rbtn_left_set(a_type, a_field, pathp[-1].node,
tnode);
} else {
rbtn_right_set(a_type, a_field, pathp[-1].node,
tnode);
}
return;
} else { \ //如果当前节点为黑色时
a_type *right = rbtn_right_get(a_type, a_field,
pathp->node);
a_type rightleft = rbtn_left_get(a_type, a_field,
right);
if (rightleft != NULL && rbtn_red_get(a_type, a_field,
rightleft)) { \ //如果当前节点为黑色时,right应该是黑色,如果rightleft是红色时
/
|| /
/
pathp(b) /
/
// \ /
/
(b) (b) /
/
/ /
/
® */
a_type tnode; \ //把rightleft着黑色,通过右旋right和左旋当前节点,使得新子树的左子树黑色节点数增加一,新子树的右子树黑色节点数不变,达到平衡。
rbtn_black_set(a_type, a_field, rightleft);
rbtn_rotate_right(a_type, a_field, right, tnode);
rbtn_right_set(a_type, a_field, pathp->node, tnode);
rbtn_rotate_left(a_type, a_field, pathp->node,
tnode);
/
Balance restored, but rotation modified /
/
subtree root, which may actually be the tree /
/
root. /
if (pathp == path) { \ //如果当前节点是根节点,重置新的根节点
/
Set root. /
rbtree->rbt_root = tnode;
} else { \ //重置父节点到新子树的根节点的连接
if (pathp[-1].cmp < 0) {
rbtn_left_set(a_type, a_field,
pathp[-1].node, tnode);
} else {
rbtn_right_set(a_type, a_field,
pathp[-1].node, tnode);
}
}
return;
} else { \ //如果当前节点为黑色时,right应该是黑色,如果rightleft是黑色时
/
|| /
/
pathp(b) /
/
// \ /
/
(b) (b) /
/
/ /
/
(b) */
a_type *tnode; \ //通过着当前节点为红色,左旋当前节点,使得新的右子树的黑色节点数也减少一,新的左子树的黑色节点数不变。这样新子树的黑色节点数量还是少一,所以这种情况平衡没调整完成,需要继续往上回溯。
rbtn_red_set(a_type, a_field, pathp->node);
rbtn_rotate_left(a_type, a_field, pathp->node,
tnode);
pathp->node = tnode;
}
}
} else { \ //if(pathp->cmp>=0) 删除黑色节点的子树出现在右子树时
a_type *left; \ //重置右子树,右子树根节点可能有调整;第一次循环的时候解决了前面提到的指向自己的问题,这里会把右子树置空。
rbtn_right_set(a_type, a_field, pathp->node,
pathp[1].node); \ //因为右子树有删除黑色节点,所以原来是平衡的RB Tree的话,左子树必不为空,并且也有相应数量的黑色节点存在。
left = rbtn_left_get(a_type, a_field, pathp->node);
if (rbtn_red_get(a_type, a_field, left)) { \ //当前节点的左孩子为红色时
a_type *tnode;
a_type *leftright = rbtn_right_get(a_type, a_field,
left);
a_type leftrightleft = rbtn_left_get(a_type, a_field,
leftright);
if (leftrightleft != NULL && rbtn_red_get(a_type,
a_field, leftrightleft)) { \ //当前节点的左孩子为红色时,左右为黑色,左右左为红色时
/
|| /
/
pathp(b) /
/
/ \ /
/
® (b) /
/
\ /
/
(b) /
/
/ /
/
® */
a_type unode; \ //经过右右左三次旋转,使得新子树的右子树增加一个黑色节点,新子树的左子树保持黑色节点数目不变。达到平衡。
rbtn_black_set(a_type, a_field, leftrightleft);
rbtn_rotate_right(a_type, a_field, pathp->node,
unode);
rbtn_rotate_right(a_type, a_field, pathp->node,
tnode);
rbtn_right_set(a_type, a_field, unode, tnode);
rbtn_rotate_left(a_type, a_field, unode, tnode);
} else { \ //当前节点的左孩子为红色时,左右为黑色,左右左为黑色时
/
|| /
/
pathp(b) /
/
/ \ /
/
® (b) /
/
\ /
/
(b) /
/
/ /
/
(b) /\ //通过右旋,并且给新的根节点着黑色,使得右子树黑色节点增加一,同时给leftright着红色,使得原子树根节点右旋后的左子树的黑色节点数也是少一个,和右子树平衡。
assert(leftright != NULL);
rbtn_red_set(a_type, a_field, leftright);
rbtn_rotate_right(a_type, a_field, pathp->node,
tnode);
rbtn_black_set(a_type, a_field, tnode);
}
/
Balance restored, but rotation modified subtree /
/
root, which may actually be the tree root. /
if (pathp == path) { \ //如果是根节点,设置新的子树根节点为树的根节点
/
Set root. */
rbtree->rbt_root = tnode;
} else { \ //重新置父节点到新子树的连接
if (pathp[-1].cmp < 0) {
rbtn_left_set(a_type, a_field, pathp[-1].node,
tnode);
} else {
rbtn_right_set(a_type, a_field, pathp[-1].node,
tnode);
}
}
return;
} else if (rbtn_red_get(a_type, a_field, pathp->node)) { \ //当前节点为红色,左孩子为黑色时;
a_type leftleft = rbtn_left_get(a_type, a_field, left);
if (leftleft != NULL && rbtn_red_get(a_type, a_field,
leftleft)) {
//当前节点为红色,左孩子为黑色时,且当前节点的左左为红色时
/
|| /
/
pathp® /
/
/ \ /
/
(b) (b) /
/
/ /
/
® */
a_type tnode; \ //着当前节点为黑色
rbtn_black_set(a_type, a_field, pathp->node); \ //着左节点为红色
rbtn_red_set(a_type, a_field, left); \ //着左左节点为黑色
rbtn_black_set(a_type, a_field, leftleft); \ //右旋当前节点为根的子树,右旋使得右子树黑色节点数量增加一,恢复平衡
rbtn_rotate_right(a_type, a_field, pathp->node,
tnode);
/
Balance restored, but rotation modified /
/
subtree root. /\ //平衡恢复,但是根节点发生了变化,需要调整根节点,因为当前节点为红色,所以肯定不是根节点。
assert((uintptr_t)pathp > (uintptr_t)path);
if (pathp[-1].cmp < 0) {
rbtn_left_set(a_type, a_field, pathp[-1].node,
tnode);
} else {
rbtn_right_set(a_type, a_field, pathp[-1].node,
tnode);
}
return;
} else { \ //当前节点为红色,左孩子为黑色时,且当前节点的左左为黑色,或者为空时
/
|| /
/
pathp® /
/
/ \ /
/
(b) (b) /
/
/ /
/
(b) /\ //把左孩子着红色,把当前节点着黑色。这样当前节点的左子树黑色也减一,保持了平衡,当前节点所在的子树因为根节点变黑,所以黑色节点数量不变。恢复了平衡,直接return。
rbtn_red_set(a_type, a_field, left);
rbtn_black_set(a_type, a_field, pathp->node);
/
Balance restored. */
return;
}
} else { \ //当前节点为黑色,左孩子为黑色时
a_type leftleft = rbtn_left_get(a_type, a_field, left);
if (leftleft != NULL && rbtn_red_get(a_type, a_field,
leftleft)) { \ //当前节点为黑色,左孩子为黑色时,且当前节点的左左为红色时
/
|| /
/
pathp(b) /
/
/ \ /
/
(b) (b) /
/
/ /
/
® */
a_type tnode; \ //左左着黑色,为右旋增加一个黑色节点做好准备
rbtn_black_set(a_type, a_field, leftleft); \ //右旋当前节点为根的子树,右旋使得右子树黑色节点数量增加一,恢复平衡,子树的根节点发生了变化
rbtn_rotate_right(a_type, a_field, pathp->node,
tnode);
/
Balance restored, but rotation modified /
/
subtree root, which may actually be the tree /
/
root. /\ //因为当前节点是黑色,所以有可能是根节点
if (pathp == path) {
/
Set root. */ \ //如果是根节点,设置新的子树根节点为树的根节点

rbtree->rbt_root = tnode;
} else { \ //重置父节点到新的子树根节点的连接,因为当前节点不再是子树根节点。
if (pathp[-1].cmp < 0) {
rbtn_left_set(a_type, a_field,
pathp[-1].node, tnode);
} else {
rbtn_right_set(a_type, a_field,
pathp[-1].node, tnode);
}
}
return;
} else { \ //当前节点为黑色,左孩子为黑色时,且当前节点的左左为黑色时
/* || /
/
pathp(b) /
/
/ \ /
/
(b) (b) /
/
/ /
/
(b) /\ //直接把左孩子着红色,使得左子树也减少一个黑色节点。这种情况需要向上继续回溯,因为子树的黑色节点还是减少了一。
rbtn_red_set(a_type, a_field, left);
}
}
}
}
/
Set root. */
rbtree->rbt_root = path->node;
assert(!rbtn_red_get(a_type, a_field, rbtree->rbt_root));
}
画图举例具体的删除过程。
在这里插入图片描述
详细文章请参考:《jemalloc 深入分析》
https://github.com/everschen/tools/blob/master/DOC/Jemalloc.pdf
https://download.csdn.net/download/ip5108/10941278

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值