3-x 插播rb tree

agenda

1. 緣由

2. rb tree properties

3. rotation

3. insertion

4. deletion

緣由

之前看到stg-stl priority queue時覺得代碼就是introduction to algorithms中大頂堆再工程化實現(xiàn)
關(guān)系型容器的一個基礎(chǔ)是rb tree, 有必要把它先弄清楚再看代碼, 這個插播系列也是給自己后面review提供一個索引

rb tree properties

properties:

A binary tree is a rb tree if it satisfies:

  1. every node is either red or black
  2. the root is black
  3. every leaf (NIL) is black
  4. if a node is red, then both it's children are black
  5. for each node, all paths from the node to descendant leaves contain the same number of black nodes.

some symbol reference:

  • it ref as p#[1-5], leaf and parent are all NIL which is black
  • l(x) imply left child of x, r(x), p(x) in the same way
  • c(x) = RED imply mark node x as RED
  • NIL(T) refer sentinel of rb tree T, root(T) is the same way
  • Case#1 ref code handle situation
  • c#[1-15] ref code from line 1 to 15

black height

bh(x): black-height of the x is the number of black nodes on any path from a node x (but not including x) down to a leaf.
from p#5 black height is well defined, since it shares all leaf of the same count.

Lemma 13.1 a rb tree with n nodes has height at most 2lg(n+1)
prove:
first we use hypothesis show subtree rooted at any node x contains at least 2^{bh(x)} - 1 internal nodes.
basis hypothesis: if h(x) = 0, then x is leaf(NIL), the subtree contains 2^{bh(x)} - 1 = 2^0 - 1 = 0 nodes, basis holds.
inductive hypothesis: for node x, h(x) > 0 with 2 children.
from p#4 --> bh(x) = \begin{cases}bh(x_{parent}), c(x_{parent}) == BLACK \\bh(x_{parent}) - 1, c(x_{parent}) == RED\end{cases} (since x_{parent} is RED x must be BLACK, excluding x make make bh(x) less 1 than bh(x_{parent}))
by hypothesis we have number of internal nodes: counts(x) \ge 2^{bh(x)} - 1
\therefore counts(x_{parent}) = counts(x_l) + count(x_r) + 1 \ge 2^{bh(x_{parent}) -1} - 1 +2^{bh(x_{parent} )- 1} - 1 + 1 = 2^{bh(x_{parent})} - 1 which prove claims
\because according to p#4 a red node must least bring a black node on the way to the leaf
\therefore at least half the nodes on any simple from the root to a leaf(not including the root) must be black. that is bh(x) \ge h(x)/2
\therefore counts(x) \ge 2^{bh(x)} - 1 \ge 2^{h/2} - 1, h \le 2lg(counts(x) + 1), that is we get upper bound of height with relation of lg(n)

rotations

a rotation operation preserves the binary search tree property: the keys in a precede key[x], key[y], and keys in r, as show in graph

void left_rotate(T, x){
  y = r(x); //buf y
  r(x) = l(y);//x's right -> β
  p(l(y)) = x; // β's parent -> x
  p(y) = p(x); // y's parent -> x's parent
  if (px(x) == NIL(T)): //x is root
    root(T) = y;
  else if (x == l(p(x))) // x is left child
    l(p(x)) = y;
  else
    r(p(x)) = y;
  l(y) = x;
  p(x) = y;
}
void right_rotate(T, y){
  x = l(y);// buf x
  l(y) = r(x);// y's left -> β
  p(r(x)) = y;// β's parent -> y
  p(x) = p(y);  x's parent -> y's parent
  if (p(y) == NIL(T)) // y is root
    root(T) = x;
  else if y == l(p(y)) // y is left child
    l(p(y)) = x;
  else
    r(p(y)) = x;
  r(x) = y;
  p(y) = x;
}

left_rotate <-> right_rotate are symmetric

insertion

insertion rb tree do as binary search tree then fix up fulfill rb tree properties.

code snap

void rb_insert(T, z){
  y = NIL(T);
  x = root(T);
  while (x != NIL(T)){ //find proper leaf position store in y
    y = x;
    if (k(z) < k(x))
      x = l(x);
    else
    x = r(x);
  }
  p(z) = y; // z's parent -> y
  if (y == NIL(T)) //not enter while loop, z is root
    root(T) = z;
  else if (k(z) < k(y))
    l(y) = z;
  else
    r(y) = z;
  }
  l(z) = r(z) = NIL(T);
  c(z) = RED;
  rb_insert_fixup(T, z);
}

the key point is rb_insert_fixup, we add new RED node at leaf position, codes as fellow:

void rb_insert_fixup(T, z){
 1  while(c(p(z)) == RED) {
 2    if (p(z) == l(p(p(x)))) {
 3      y = r(p(p(z))); 
 4      if (c(y) == RED){ //Case#1
 5        c(p(z)) = BLACK;
 6        c(y) = BLACK;
 7        c(p(p(z))) = RED;
 8        z = p(p(z));
 9     } else if (z == r(p(z))){ //Case#2
10      z = p(z);
11      left_rotate(T, z);
12    else { c(p(z)) = BLACK;//Case#3
13       c(p(p(z))) = RED;
14      right_rotate(T, p(p(z)));
15  else ... exchange `left` <-> `right` do line c#2-14
16  c(root(T)) = BLACK;  
}

correctness

FIRST we see by call of rb_insert, it results violates p#2,4

  1. p#2 is break when z is root, c#16 fix it
  2. p#4 is break when z and p(z) are both RED

for p#1,3,5 does not affect(since insert one is RED node)

SECOND we check loop invariant before rb_insert_fixup and at each case of start of iteration, c#1-15 maintains the fellowing three part loop invariant are:
a. z is RED
b. if p(z) is the root, p(z) is BLACK (imply p(z) is RED, p(p(x)) exists)
c. p#2,4 at most one violation.
loop invariant is true prior to the first iteration of the loop. each loop maintains the loop, it make legal rb tree at loop termination.

Initialization (prior call of rb_insert_fixup)
a. newly add z is RED
b. if p(z) is root, p(z) started out BLACK not change prior the call of rb_insert_fixup
c. p#1,3,5 hold when rb_insert_fixup is called
?1. if p#2 is not hold, z must be root, it's children are the sentinel, which is BLACK, not a violation of p#4
?2. if p#4 is not hold, z & p(z) are RED, the tree had no other violations prior to z, hence not violation of p#2
thus p#2 & p#4 at most one violation.

Termination (loop finish)
when loop terminates, p(z) is BLACK, no violation of p#4, p#2 is fixed by c#16, when rb_insert_fixup terminates all rb tree properties hold.

Maintains (each case)
there are 6 cases of cases, 3 of then are symmetric (depend on p(z) is left or right of p(p(z)))
from c#2 check p(z) == RED because b of loop invariant, p(z) can not be root, that's p(p(z)) is exists, otherwise (p(z) == BLACK) loop terminates

Case#1
distinguish from Case2,3 by uncle's color
In Case#1 p(z), y, z are RED, p(p(z)) is BLACK, making c(y) = c(p(z)) = BLACK, p(p(z)) = RED maintains p#5 then repeat z = p(p(z)) move up 2 levels.
as shows:

loop invariant
a. after loop, new z = z' is p(p(z)) is RED
b. if p(z') is root, and it not affected in this loop, remain BLACK at the next iteration
c. Case#1 maintain p#5, not introduce p#1,3
? if z' is root, violation of p#2 only
? if z' is not root, z''s color is not related with p#2, we color p(z) and y to BLACK to fix z and p(z) both RED thereby maintain p#5
? if p(z') is BLACK rb tree properties hold, otherwise introduce violation of p#4 only and loop continue

Case2,3
In Case2,3, y is BLACK, then check z is left/right of p(z)'s child, if z == r(p(z)) calling left_rotate transform from Case#2 to Case#3, else enter directly into Case#3
In Case2,3, because both z and p(z) are RED, the rotation affects neither the black height of nodes nor p#5 when y is BLACK
? in Case#2, c#10 move up one level and down one level in c#11 the identity of p(p(z)) remain unchanged.
? in Case#3, color change and right rotation preserve p#5, no longer have 2 RED in one row, p(z) is BLACK loop terminates.

loop invariant
a. Case#2 while loop finish z now points previous p(z)(in graph node A) which is RED, no further change z and it's color
b. Case#3 make p(z) to BLACK, if p(z) is root, at next start of iteration, it's BLACK
c. p#1,3,5 are maintained in Case#2,3
? z is not root in Case#2,3, no way of violation p#2, only paint RED in Case#3 becomes child of BLACK node no way of violation p#4
? Case#2 -> Case#3 which only violation of p#4 without another violation

Analysis

rb_insert running time is O(lg(n))
rb_insert_fixup running time is O(lg(n))
? Case#1 is executed, z moves up 2 levels up
? Case#2 -> Case#3 -> finish
? Case#3 -> finish

deletion

code snap

 1  void rb_delete(T, z) {
 2    y = (l(z) == NIL(T) or r(z) == NIL(T)) ? z : tree_successor(z);
 3    x =  (l(y) != NIL(T)) ? l(y) : r(y);
 4    if (p(y) == NIL(T))
 5      ROOT(T) = x;
 6    else if y == l(p(y))
 7       l(p(y)) = x;
 8    else
 9      r(p(y)) = x;
10    if (y != z)
11      k(z) = k(y);
12      //cp y's data into z
13    if (c(y) == BLACK)
14      rb_delete_fixup(T, x);
15    return y;
16  }

 1  void rb_delete_fixup(T, x) {
 2    while ( x != ROOT(t) and c(x) == BLACK){
 3      if (x == l(p(x)) {
 4        w = r(p(x));
 5        if (c(w) == RED) { //Case#1
 6          c(w) = BLACK;
 7          c(p(w)) = RED;
 8          left_rotate(T, p(x));
 9          w = r(p(x));
10        }
11        if (c(l(w) == BLACK and c(r(w)) == BLKACK){ //Case#2
12          c(w) = RED;
13          x = p(x);
14        }
15        else if (c(r(w)) == BLACK) { Case#3
16          c(l(w)) = BLACK;
17          c(w) = RED;
18          right_rotate(T, w);
19          w = r(p(x));
20        }
21        else {
22          c(w) = c(p(x));
23          c(p(x)) = BLACK;
24          c(r(w)) = BLACK;
25          left_rorate(T, p(x));
26          x = ROOT(T);
27        }
28    else // "right" <-> "left" do c#3-27
29    c(x) = BLACK;
30  }

correctness

in rb_delete:

  • y is target delete note
  • x is child of y which may break rb tree properties because deleting it's parent y

rb_delete intents splice y by x, there 3 cases:

  1. y is leaf node, delete it directly
  2. y has on child, splice y with it's child x
  3. y has two children, delete position of it's successor, copy key & data into y 's position.
    leaf

    one child

    two children

if spliced node y is RED, the rb properties still hold:

  • no black-heights in the tree have changed.
  • no red nodes have been made adjacent.
  • y is not root, root's color remain.

if spliced node y is BLACK, he rb propeties broken as:

  • y is root, x became new root, violation of p#2
  • if p(y) and x are RED, splice y violates p#4
  • remove BLACK y cause p#5, we set node point by x either double-black (when color is BLACK, black count is 2) or red-black (when color is RED, black count is 1), in that way p#5 still hold, p#1 is broken.

rb_delete_fixup restores p#1,2,4, maintain p#5
while loop move the extra black until:

  1. x points to red-black node, set black @ c#29
  2. x points to root, set black @ c#29
  3. suitable rotations and recoloring can be performaced

there are 4 cases depends on x 's is root and color

  1. if x is new root original is RED/BLACK, if x is not root and original RED: since y is BLACK, c#29 ensure p#1,2,4, remove y BLACK then adding new BLACK p#5 preserves
  2. if x is not root and original BLACK, there are another 4 cases, c#29 ensure no violation of p#1,2, since x is BLACK, in while loop c#2-28 x start points double-black node maintain p#5

Case#1

  1. after remove back node of y, count(A) = 2 makes p#5 still holds.
  2. subtree α, β, r, g, ε, ξ 's black height is not change before and after transformation.
  3. after transformation, w's color change to BLACK, then transfer to Case#[2-4]
  4. since w is RED, l(w) & r(w) must exists and BLACK for p#5 holds before transformation

Case#2


both l(w) & r(w) are BLACK

  1. w must be BLACK, otherwise Case#1 is executed first.
    2.before transformation: x points to double-black count(A) = 2, after transformation: x points either double-black or red-black node, count(A) decrease 1 then count(B) increase 1 letting bh(α) = bh(B) + 2 not changed, in the same way r, g, ε, ξ bh is not change(count(D) decrease 1 then count(B) increase 1) -> p#5 preserve.
  2. if B is RED making D RED exit while loop , c#29 making B BLACK p#4 preserve, if B is BLACK loop continue p#4 holds(transform from Case#1)

Case#3


w is BLACK, r(w) is BLACK, l(w) i RED

  1. bh of α, β, r, g, ε, ξ is not changed p#4 holds
  2. since l(w) is RED, c(g) must be BLACK, making C(D) = RED no violation of ##p#4##
  3. after ##Case#3## c(r(w)) is RED then transfer to ##Case#4##

Case#4


c(D) is BLACK, c(C) & c(E) is RED

  1. since set x as root then set BLACK, count(A) from 2 to 1, then adding new BLACK B, bh is maintained
  2. α, β, r, g, ε, ξ is not changed, ##p#5## is maintained
  3. make x as root loop terminate, c#29 preserve ##p#1,2##

Analysis

  1. rb_delete_fixup ##Case#1,3,4## operations of constant time
    2.Case#2 move up by 1 level, and continue when p(x) is RED at most execute lg(n) times.
  2. tree_successor running time is O(lg(n)), overall rb_delete running time is O(lg(n))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市忘朝,隨后出現(xiàn)的幾起案子沉噩,更是在濱河造成了極大的恐慌胖烛,老刑警劉巖邑飒,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胁编,死亡現(xiàn)場離奇詭異馒稍,居然都是意外死亡礁哄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門铜涉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智玻,“玉大人,你說我怎么就攤上這事芙代〉跎荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵纹烹,是天一觀的道長页滚。 經(jīng)常有香客問我,道長铺呵,這世上最難降的妖魔是什么裹驰? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮片挂,結(jié)果婚禮上幻林,老公的妹妹穿的比我還像新娘。我一直安慰自己音念,他們只是感情好沪饺,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闷愤,像睡著了一般随闽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肝谭,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機(jī)與錄音蛾扇,去河邊找鬼攘烛。 笑死,一個胖子當(dāng)著我的面吹牛镀首,可吹牛的內(nèi)容都是我干的坟漱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼更哄,長吁一口氣:“原來是場噩夢啊……” “哼芋齿!你這毒婦竟也來了腥寇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤觅捆,失蹤者是張志新(化名)和其女友劉穎赦役,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栅炒,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡掂摔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赢赊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乙漓。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖释移,靈堂內(nèi)的尸體忽然破棺而出叭披,到底是詐尸還是另有隱情,我是刑警寧澤玩讳,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布涩蜘,位于F島的核電站,受9級特大地震影響锋边,放射性物質(zhì)發(fā)生泄漏皱坛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一豆巨、第九天 我趴在偏房一處隱蔽的房頂上張望剩辟。 院中可真熱鬧,春花似錦往扔、人聲如沸贩猎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吭服。三九已至,卻和暖如春蝗罗,著一層夾襖步出監(jiān)牢的瞬間艇棕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工串塑, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留沼琉,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓桩匪,卻偏偏與公主長得像打瘪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Goal: get a search tree data structure that we can insert...
    邢昱閱讀 427評論 0 0
  • DefinitionA red-black tree (RBT) is a binary search tree ...
    何大炮閱讀 255評論 5 0
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,322評論 0 10
  • 紅黑樹 紅黑樹是一種特殊的二叉查找樹(binary search tree闺骚,以下簡稱BST)彩扔,它用來解決BST的致...
    山里沒有經(jīng)閱讀 221評論 0 1
  • 字符串 1.什么是字符串 使用單引號或者雙引號括起來的字符集就是字符串。 引號中單獨的符號僻爽、數(shù)字虫碉、字母等叫字符。 ...
    mango_2e17閱讀 7,507評論 1 7