日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

[wiki]红黑树

發(fā)布時(shí)間:2025/7/14 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [wiki]红黑树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

紅黑樹

維基百科,自由的百科全書

紅黑樹是一種自平衡二叉查找樹,是在計(jì)算機(jī)科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實(shí)現(xiàn)關(guān)聯(lián)數(shù)組。它是在1972年由魯?shù)婪颉へ悹柊l(fā)明的,他稱之為"對(duì)稱二叉B樹",它現(xiàn)代的名字是在 Leo J. Guibas 和?Robert Sedgewick?于1978年寫的一篇論文中獲得的。它是復(fù)雜的,但它的操作有著良好的最壞情況運(yùn)行時(shí)間,并且在實(shí)踐中是高效的: 它可以在O(log?n)時(shí)間內(nèi)做查找,插入和刪除,這里的n是樹中元素的數(shù)目。


紅黑樹和AVL樹一樣都對(duì)插入時(shí)間、刪除時(shí)間和查找時(shí)間提供了最好可能的最壞情況擔(dān)保。這不只是使它們?cè)跁r(shí)間敏感的應(yīng)用如實(shí)時(shí)應(yīng)用(real time application)中有價(jià)值,而且使它們有在提供最壞情況擔(dān)保的其他數(shù)據(jù)結(jié)構(gòu)中作為建造板塊的價(jià)值;例如,在計(jì)算幾何中使用的很多數(shù)據(jù)結(jié)構(gòu)都可以基于紅黑樹。[編輯]
用途和好處

紅黑樹在函數(shù)式編程中也特別有用,在這里它們是最常用的持久數(shù)據(jù)結(jié)構(gòu)之一,它們用來構(gòu)造關(guān)聯(lián)數(shù)組和集合,在突變之后它們能保持為以前的版本。除了O(log?n)的時(shí)間之外,紅黑樹的持久版本對(duì)每次插入或刪除需要O(log?n)的空間。

紅黑樹是?2-3-4樹的一種等同。換句話說,對(duì)于每個(gè) 2-3-4 樹,都存在至少一個(gè)數(shù)據(jù)元素是同樣次序的紅黑樹。在 2-3-4 樹上的插入和刪除操作也等同于在紅黑樹中顏色翻轉(zhuǎn)和旋轉(zhuǎn)。這使得 2-3-4 樹成為理解紅黑樹背后的邏輯的重要工具,這也是很多介紹算法的教科書在紅黑樹之前介紹 2-3-4 樹的原因,盡管 2-3-4 樹在實(shí)踐中不經(jīng)常使用。

[編輯]性質(zhì)

紅黑樹是每個(gè)節(jié)點(diǎn)都帶有顏色屬性的二叉查找樹,顏色為紅色或黑色。在二叉查找樹強(qiáng)制一般要求以外,對(duì)于任何有效的紅黑樹我們?cè)黾恿巳缦碌念~外要求:

性質(zhì)1. 節(jié)點(diǎn)是紅色或黑色。

性質(zhì)2. 根是黑色。

性質(zhì)3. 所有葉子都是黑色(葉子是NIL節(jié)點(diǎn))。

性質(zhì)4. 每個(gè)紅色節(jié)點(diǎn)的兩個(gè)子節(jié)點(diǎn)都是黑色。(從每個(gè)葉子到根的所有路徑上不能有兩個(gè)連續(xù)的紅色節(jié)點(diǎn))

性質(zhì)5. 從任一節(jié)點(diǎn)到其每個(gè)葉子的所有簡單路徑都包含相同數(shù)目的黑色節(jié)點(diǎn)。

這些約束強(qiáng)制了紅黑樹的關(guān)鍵性質(zhì): 從根到葉子的最長的可能路徑不多于最短的可能路徑的兩倍長。結(jié)果是這個(gè)樹大致上是平衡的。因?yàn)椴僮鞅热绮迦搿h除和查找某個(gè)值的最壞情況時(shí)間都要求與樹的高度成比例,這個(gè)在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同于普通的二叉查找樹。

要知道為什么這些特性確保了這個(gè)結(jié)果,注意到屬性4導(dǎo)致了路徑不能有兩個(gè)毗連的紅色節(jié)點(diǎn)就足夠了。最短的可能路徑都是黑色節(jié)點(diǎn),最長的可能路徑有交替的紅色和黑色節(jié)點(diǎn)。因?yàn)楦鶕?jù)屬性5所有最長的路徑都有相同數(shù)目的黑色節(jié)點(diǎn),這就表明了沒有路徑能多于任何其他路徑的兩倍長。

在很多樹數(shù)據(jù)結(jié)構(gòu)的表示中,一個(gè)節(jié)點(diǎn)有可能只有一個(gè)子節(jié)點(diǎn),而葉子節(jié)點(diǎn)包含數(shù)據(jù)。用這種范例表示紅黑樹是可能的,但是這會(huì)改變一些屬性并使算法復(fù)雜。為此,本文中我們使用 "nil 葉子" 或"空(null)葉子",如上圖所示,它不包含數(shù)據(jù)而只充當(dāng)樹在此結(jié)束的指示。這些節(jié)點(diǎn)在繪圖中經(jīng)常被省略,導(dǎo)致了這些樹好像同上述原則相矛盾,而實(shí)際上不是這樣。與此有關(guān)的結(jié)論是所有節(jié)點(diǎn)都有兩個(gè)子節(jié)點(diǎn),盡管其中的一個(gè)或兩個(gè)可能是空葉子。

[編輯]操作

因?yàn)槊恳粋€(gè)紅黑樹也是一個(gè)特化的二叉查找樹,因此紅黑樹上的只讀操作與普通二叉查找樹上的只讀操作相同。然而,在紅黑樹上進(jìn)行插入操作和刪除操作會(huì)導(dǎo)致不再符合紅黑樹的性質(zhì)。恢復(fù)紅黑樹的屬性需要少量(O(logn))的顏色變更(實(shí)際是非常快速的)和不超過三次樹旋轉(zhuǎn)(對(duì)于插入操作是兩次)。雖然插入和刪除很復(fù)雜,但操作時(shí)間仍可以保持為 O(log?n) 次。

[編輯]插入

我們首先以二叉查找樹的方法增加節(jié)點(diǎn)并標(biāo)記它為紅色。(如果設(shè)為黑色,就會(huì)導(dǎo)致根到葉子的路徑上有一條路上,多一個(gè)額外的黑節(jié)點(diǎn),這個(gè)是很難調(diào)整的。但是設(shè)為紅色節(jié)點(diǎn)后,可能會(huì)導(dǎo)致出現(xiàn)兩個(gè)連續(xù)紅色節(jié)點(diǎn)的沖突,那么可以通過顏色調(diào)換(color flips)和樹旋轉(zhuǎn)來調(diào)整。) 下面要進(jìn)行什么操作取決于其他臨近節(jié)點(diǎn)的顏色。同人類的家族樹中一樣,我們將使用術(shù)語叔父節(jié)點(diǎn)來指一個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)的兄弟節(jié)點(diǎn)。注意:

  • 性質(zhì)1[1]和性質(zhì)3[2]總是保持著。
  • 性質(zhì)4[3]只在增加紅色節(jié)點(diǎn)、重繪黑色節(jié)點(diǎn)為紅色,或做旋轉(zhuǎn)時(shí)受到威脅。
  • 性質(zhì)5[4]只在增加黑色節(jié)點(diǎn)、重繪紅色節(jié)點(diǎn)為黑色,或做旋轉(zhuǎn)時(shí)受到威脅。

在下面的示意圖中,將要插入的節(jié)點(diǎn)標(biāo)為NN的父節(jié)點(diǎn)標(biāo)為PN的祖父節(jié)點(diǎn)標(biāo)為GN的叔父節(jié)點(diǎn)標(biāo)為U。在圖中展示的任何顏色要么是由它所處情形所作的假定,要么是這些假定所暗含 (imply) 的。

對(duì)于每一種情況,我們將使用?C?示例代碼來展示。通過下列函數(shù),可以找到一個(gè)節(jié)點(diǎn)的叔父和祖父節(jié)點(diǎn):

node grandparent(node n) {return n->parent->parent;}node uncle(node n) {if (n->parent == grandparent(n)->left)return grandparent(n)->right;elsereturn grandparent(n)->left;}

情形1: 新節(jié)點(diǎn)N位于樹的根上,沒有父節(jié)點(diǎn)。在這種情形下,我們把它重繪為黑色以滿足性質(zhì)2[5]。因?yàn)樗诿總€(gè)路徑上對(duì)黑節(jié)點(diǎn)數(shù)目增加一,性質(zhì)5[4]符合。

void insert_case1(node n) {if (n->parent == NULL)n->color = BLACK;elseinsert_case2(n);}

情形2: 新節(jié)點(diǎn)的父節(jié)點(diǎn)P是黑色,所以性質(zhì)4[3]沒有失效(新節(jié)點(diǎn)是紅色的)。在這種情形下,樹仍是有效的。性質(zhì)5[4]也未受到威脅,盡管新節(jié)點(diǎn)N有兩個(gè)黑色葉子子節(jié)點(diǎn);但由于新節(jié)點(diǎn)N是紅色,通過它的每個(gè)子節(jié)點(diǎn)的路徑就都有同通過它所取代的黑色的葉子的路徑同樣數(shù)目的黑色節(jié)點(diǎn),所以依然滿足這個(gè)性質(zhì)。

void insert_case2(node n) {if (n->parent->color == BLACK)return; /* 樹仍舊有效 */elseinsert_case3(n);}

注意:?在下列情形下我們假定新節(jié)點(diǎn)的父節(jié)點(diǎn)為紅色,所以它有祖父節(jié)點(diǎn);因?yàn)槿绻腹?jié)點(diǎn)是根節(jié)點(diǎn),那父節(jié)點(diǎn)就應(yīng)當(dāng)是黑色。所以新節(jié)點(diǎn)總有一個(gè)叔父節(jié)點(diǎn),盡管在情形4和5下它可能是葉子節(jié)點(diǎn)。

情形3: 如果父節(jié)點(diǎn)P和叔父節(jié)點(diǎn)U二者都是紅色,(此時(shí)新插入節(jié)點(diǎn)N做為P的左子節(jié)點(diǎn)或右子節(jié)點(diǎn)都屬于情形3,這里右圖僅顯示N做為P左子的情形)則我們可以將它們兩個(gè)重繪為黑色并重繪祖父節(jié)點(diǎn)G為紅色(用來保持性質(zhì)5[4])。現(xiàn)在我們的新節(jié)點(diǎn)N有了一個(gè)黑色的父節(jié)點(diǎn)P。因?yàn)橥ㄟ^父節(jié)點(diǎn)P或叔父節(jié)點(diǎn)U的任何路徑都必定通過祖父節(jié)點(diǎn)G,在這些路徑上的黑節(jié)點(diǎn)數(shù)目沒有改變。但是,紅色的祖父節(jié)點(diǎn)G的父節(jié)點(diǎn)也有可能是紅色的,這就違反了性質(zhì)4[3]。為了解決這個(gè)問題,我們?cè)谧娓腹?jié)點(diǎn)G上遞歸地進(jìn)行情形1的整個(gè)過程。(把G當(dāng)成是新加入的節(jié)點(diǎn)進(jìn)行各種情況的檢查)

void insert_case3(node n) {if (uncle(n) != NULL && uncle(n)->color == RED) {n->parent->color = BLACK;uncle(n)->color = BLACK;grandparent(n)->color = RED;insert_case1(grandparent(n));}elseinsert_case4(n);}

注意:?在余下的情形下,我們假定父節(jié)點(diǎn)P?是其父親G?的左子節(jié)點(diǎn)。如果它是右子節(jié)點(diǎn),情形4情形5中的左和右應(yīng)當(dāng)對(duì)調(diào)。

情形4: 父節(jié)點(diǎn)P是紅色而叔父節(jié)點(diǎn)U是黑色,并且新節(jié)點(diǎn)N是其父節(jié)點(diǎn)P的右子節(jié)點(diǎn)而父節(jié)點(diǎn)P又是其父節(jié)點(diǎn)的左子節(jié)點(diǎn)。在這種情形下,我們進(jìn)行一次左旋轉(zhuǎn)調(diào)換新節(jié)點(diǎn)和其父節(jié)點(diǎn)的角色; 接著,我們按情形5處理以前的父節(jié)點(diǎn)P以解決仍然失效的性質(zhì)4[3]。注意這個(gè)改變會(huì)導(dǎo)致某些路徑通過它們以前不通過的新節(jié)點(diǎn)N(比如圖中1號(hào)葉子節(jié)點(diǎn))或不通過節(jié)點(diǎn)P(比如圖中3號(hào)葉子節(jié)點(diǎn)),但由于這兩個(gè)節(jié)點(diǎn)都是紅色的,所以性質(zhì)5[4]仍有效。

void insert_case4(node n) {if (n == n->parent->right && n->parent == grandparent(n)->left) {rotate_left(n->parent);n = n->left;} else if (n == n->parent->left && n->parent == grandparent(n)->right) {rotate_right(n->parent);n = n->right;}insert_case5(n);}

情形5: 父節(jié)點(diǎn)P是紅色而叔父節(jié)點(diǎn)U?是黑色或缺少,新節(jié)點(diǎn)N?是其父節(jié)點(diǎn)的左子節(jié)點(diǎn),而父節(jié)點(diǎn)P又是其父節(jié)點(diǎn)G的左子節(jié)點(diǎn)。在這種情形下,我們進(jìn)行針對(duì)祖父節(jié)點(diǎn)G?的一次右旋轉(zhuǎn); 在旋轉(zhuǎn)產(chǎn)生的樹中,以前的父節(jié)點(diǎn)P現(xiàn)在是新節(jié)點(diǎn)N和以前的祖父節(jié)點(diǎn)G?的父節(jié)點(diǎn)。我們知道以前的祖父節(jié)點(diǎn)G是黑色,否則父節(jié)點(diǎn)P就不可能是紅色 (如果?P?和?G都是紅色就違反了性質(zhì)4,所以?G?必須是黑色)。我們切換以前的父節(jié)點(diǎn)P和祖父節(jié)點(diǎn)G的顏色,結(jié)果的樹滿足性質(zhì)4[3]。性質(zhì)5[4]也仍然保持滿足,因?yàn)橥ㄟ^這三個(gè)節(jié)點(diǎn)中任何一個(gè)的所有路徑以前都通過祖父節(jié)點(diǎn)G?,現(xiàn)在它們都通過以前的父節(jié)點(diǎn)P。在各自的情形下,這都是三個(gè)節(jié)點(diǎn)中唯一的黑色節(jié)點(diǎn)。

void insert_case5(node n) {n->parent->color = BLACK;grandparent(n)->color = RED;if (n == n->parent->left && n->parent == grandparent(n)->left) {rotate_right(grandparent(n));} else {/* Here, n == n->parent->right && n->parent == grandparent(n)->right */rotate_left(grandparent(n));}}

注意插入實(shí)際上是原地算法,因?yàn)樯鲜鏊姓{(diào)用都使用了尾部遞歸。

[編輯]刪除

如果需要?jiǎng)h除的節(jié)點(diǎn)有兩個(gè)兒子,那么問題可以被轉(zhuǎn)化成刪除另一個(gè)只有一個(gè)兒子的節(jié)點(diǎn)的問題(為了表述方便,這里所指的兒子,為非葉子節(jié)點(diǎn)的兒子)。對(duì)于二叉查找樹,在刪除帶有兩個(gè)非葉子兒子的節(jié)點(diǎn)的時(shí)候,我們找到要么在它的左子樹中的最大元素、要么在它的右子樹中的最小元素,并把它的值轉(zhuǎn)移到要?jiǎng)h除的節(jié)點(diǎn)中(如在這里所展示的那樣)。我們接著刪除我們從中復(fù)制出值的那個(gè)節(jié)點(diǎn),它必定有少于兩個(gè)非葉子的兒子。因?yàn)橹皇菑?fù)制了一個(gè)值而不違反任何屬性,這就把問題簡化為如何刪除最多有一個(gè)兒子的節(jié)點(diǎn)的問題。它不關(guān)心這個(gè)節(jié)點(diǎn)是最初要?jiǎng)h除的節(jié)點(diǎn)還是我們從中復(fù)制出值的那個(gè)節(jié)點(diǎn)。

在本文余下的部分中,我們只需要討論刪除只有一個(gè)兒子的節(jié)點(diǎn)(如果它兩個(gè)兒子都為空,即均為葉子,我們?nèi)我鈱⑵渲幸粋€(gè)看作它的兒子)。如果我們刪除一個(gè)紅色節(jié)點(diǎn)(此時(shí)該節(jié)點(diǎn)的兒子將都為葉子節(jié)點(diǎn)),它的父親和兒子一定是黑色的。所以我們可以簡單的用它的黑色兒子替換它,并不會(huì)破壞屬性3和4。通過被刪除節(jié)點(diǎn)的所有路徑只是少了一個(gè)紅色節(jié)點(diǎn),這樣可以繼續(xù)保證屬性5。另一種簡單情況是在被刪除節(jié)點(diǎn)是黑色而它的兒子是紅色的時(shí)候。如果只是去除這個(gè)黑色節(jié)點(diǎn),用它的紅色兒子頂替上來的話,會(huì)破壞屬性5,但是如果我們重繪它的兒子為黑色,則曾經(jīng)通過它的所有路徑將通過它的黑色兒子,這樣可以繼續(xù)保持屬性5。

需要進(jìn)一步討論的是在要?jiǎng)h除的節(jié)點(diǎn)和它的兒子二者都是黑色的時(shí)候,這是一種復(fù)雜的情況。我們首先把要?jiǎng)h除的節(jié)點(diǎn)替換為它的兒子。出于方便,稱呼這個(gè)兒子為N,稱呼它的兄弟(它父親的另一個(gè)兒子)為S。在下面的示意圖中,我們還是使用P稱呼N的父親,SL稱呼S的左兒子,SR稱呼S的右兒子。我們將使用下述函數(shù)找到兄弟節(jié)點(diǎn):

struct node * sibling(struct node *n) {if (n == n->parent->left)return n->parent->right;elsereturn n->parent->left; }

我們可以使用下列代碼進(jìn)行上述的概要步驟,這里的函數(shù)?replace_node?替換?child?到?n?在樹中的位置。出于方便,在本章節(jié)中的代碼將假定空葉子被用不是 NULL 的實(shí)際節(jié)點(diǎn)對(duì)象來表示(在插入章節(jié)中的代碼可以同任何一種表示一起工作)。

void delete_one_child(struct node *n) {/** Precondition: n has at most one non-null child.*/struct node *child = is_leaf(n->right) ? n->left : n->right;replace_node(n, child);if (n->color == BLACK) {if (child->color == RED)child->color = BLACK;elsedelete_case1(child);}free(n); }

如果 N 和它初始的父親是黑色,則刪除它的父親導(dǎo)致通過 N 的路徑都比不通過它的路徑少了一個(gè)黑色節(jié)點(diǎn)。因?yàn)檫@違反了屬性 4,樹需要被重新平衡。有幾種情況需要考慮:

情況 1:?N 是新的根。在這種情況下,我們就做完了。我們從所有路徑去除了一個(gè)黑色節(jié)點(diǎn),而新根是黑色的,所以屬性都保持著。

void delete_case1(struct node *n) {if (n->parent != NULL)delete_case2(n); }

注意: 在情況2、5和6下,我們假定 N 是它父親的左兒子。如果它是右兒子,則在這些情況下的左和右應(yīng)當(dāng)對(duì)調(diào)。

情況 2:?S 是紅色。在這種情況下我們?cè)贜的父親上做左旋轉(zhuǎn),把紅色兄弟轉(zhuǎn)換成N的祖父。我們接著對(duì)調(diào) N 的父親和祖父的顏色。盡管所有的路徑仍然有相同數(shù)目的黑色節(jié)點(diǎn),現(xiàn)在 N 有了一個(gè)黑色的兄弟和一個(gè)紅色的父親,所以我們可以接下去按 4、5或6情況來處理。(它的新兄弟是黑色因?yàn)樗羌t色S的一個(gè)兒子。)

void delete_case2(struct node *n) {struct node *s = sibling(n);if (s->color == RED) {n->parent->color = RED;s->color = BLACK;if (n == n->parent->left)rotate_left(n->parent);elserotate_right(n->parent);} delete_case3(n); }

情況 3:?N 的父親、S 和 S 的兒子都是黑色的。在這種情況下,我們簡單的重繪 S 為紅色。結(jié)果是通過S的所有路徑,它們就是以前不通過 N 的那些路徑,都少了一個(gè)黑色節(jié)點(diǎn)。因?yàn)閯h除 N 的初始的父親使通過 N 的所有路徑少了一個(gè)黑色節(jié)點(diǎn),這使事情都平衡了起來。但是,通過 P 的所有路徑現(xiàn)在比不通過 P 的路徑少了一個(gè)黑色節(jié)點(diǎn),所以仍然違反屬性4。要修正這個(gè)問題,我們要從情況 1 開始,在 P 上做重新平衡處理。

void delete_case3(struct node *n) {struct node *s = sibling(n);if ((n->parent->color == BLACK) &&(s->color == BLACK) &&(s->left->color == BLACK) &&(s->right->color == BLACK)) {s->color = RED;delete_case1(n->parent);} elsedelete_case4(n); }

情況 4:?S 和 S 的兒子都是黑色,但是 N 的父親是紅色。在這種情況下,我們簡單的交換 N 的兄弟和父親的顏色。這不影響不通過 N 的路徑的黑色節(jié)點(diǎn)的數(shù)目,但是它在通過 N 的路徑上對(duì)黑色節(jié)點(diǎn)數(shù)目增加了一,添補(bǔ)了在這些路徑上刪除的黑色節(jié)點(diǎn)。

void delete_case4(struct node *n) {struct node *s = sibling(n);if ((n->parent->color == RED) &&(s->color == BLACK) &&(s->left->color == BLACK) &&(s->right->color == BLACK)) {s->color = RED;n->parent->color = BLACK;} elsedelete_case5(n); }

情況 5:?S 是黑色,S 的左兒子是紅色,S 的右兒子是黑色,而 N 是它父親的左兒子。在這種情況下我們?cè)?S 上做右旋轉(zhuǎn),這樣 S 的左兒子成為 S 的父親和 N 的新兄弟。我們接著交換 S 和它的新父親的顏色。所有路徑仍有同樣數(shù)目的黑色節(jié)點(diǎn),但是現(xiàn)在 N 有了一個(gè)右兒子是紅色的黑色兄弟,所以我們進(jìn)入了情況 6。N 和它的父親都不受這個(gè)變換的影響。

void delete_case5(struct node *n) {struct node *s = sibling(n);if (s->color == BLACK) /* this if statement is trivial, due to Case 2 (even though Case two changed the sibling to a sibling's child, the sibling's child can't be red, since no red parent can have a red child). */ // the following statements just force the red to be on the left of the left of the parent, // or right of the right, so case six will rotate correctly.if ((n == n->parent->left) &&(s->right->color == BLACK) &&(s->left->color == RED)) { // this last test is trivial too due to cases 2-4.s->color = RED;s->left->color = BLACK;rotate_right(s);} else if ((n == n->parent->right) &&(s->left->color == BLACK) &&(s->right->color == RED)) {// this last test is trivial too due to cases 2-4.s->color = RED;s->right->color = BLACK;rotate_left(s);}}delete_case6(n); }

情況 6:?S 是黑色,S 的右兒子是紅色,而 N 是它父親的左兒子。在這種情況下我們?cè)?N 的父親上做左旋轉(zhuǎn),這樣 S 成為 N 的父親和 S 的右兒子的父親。我們接著交換 N 的父親和 S 的顏色,并使 S 的右兒子為黑色。子樹在它的根上的仍是同樣的顏色,所以屬性 3 沒有被違反。但是,N 現(xiàn)在增加了一個(gè)黑色祖先: 要么 N 的父親變成黑色,要么它是黑色而 S 被增加為一個(gè)黑色祖父。所以,通過 N 的路徑都增加了一個(gè)黑色節(jié)點(diǎn)。

此時(shí),如果一個(gè)路徑不通過 N,則有兩種可能性:

  • 它通過 N 的新兄弟。那么它以前和現(xiàn)在都必定通過 S 和 N 的父親,而它們只是交換了顏色。所以路徑保持了同樣數(shù)目的黑色節(jié)點(diǎn)。
  • 它通過 N 的新叔父,S 的右兒子。那么它以前通過 S、S 的父親和 S 的右兒子,但是現(xiàn)在只通過 S,它被假定為它以前的父親的顏色,和 S 的右兒子,它被從紅色改變?yōu)楹谏:铣尚Ч沁@個(gè)路徑通過了同樣數(shù)目的黑色節(jié)點(diǎn)。

在任何情況下,在這些路徑上的黑色節(jié)點(diǎn)數(shù)目都沒有改變。所以我們恢復(fù)了屬性 4。在示意圖中的白色節(jié)點(diǎn)可以是紅色或黑色,但是在變換前后都必須指定相同的顏色。

void delete_case6(struct node *n) {struct node *s = sibling(n);s->color = n->parent->color;n->parent->color = BLACK;if (n == n->parent->left) {s->right->color = BLACK;rotate_left(n->parent);} else {s->left->color = BLACK;rotate_right(n->parent);} }

同樣的,函數(shù)調(diào)用都使用了尾部遞歸,所以算法是就地的。此外,在旋轉(zhuǎn)之后不再做遞歸調(diào)用,所以進(jìn)行了恒定數(shù)目(最多 3 次)的旋轉(zhuǎn)。

[編輯]漸進(jìn)邊界的證明

包含n個(gè)內(nèi)部節(jié)點(diǎn)的紅黑樹的高度是 O(log(n))。

定義:

  • h(v) = 以節(jié)點(diǎn)v為根的子樹的高度。
  • bh(v) = 從v到子樹中任何葉子的黑色節(jié)點(diǎn)的數(shù)目(如果v是黑色則不計(jì)數(shù)它)(也叫做黑色高度)。

引理:?以節(jié)點(diǎn)v為根的子樹有至少2bh(v)?? 1個(gè)內(nèi)部節(jié)點(diǎn)。

引理的證明(通過歸納高度):

基礎(chǔ): h(v) = 0

如果v的高度是零則它必定是 nil,因此 bh(v) = 0。所以:

2bh(v)?? 1 = 20?? 1 = 1 ? 1 = 0

歸納假設(shè): h(v) = k 的v有?2bh(v) ? 1?? 1?個(gè)內(nèi)部節(jié)點(diǎn)暗示了 h(v') = k+1 的?v'2bh(v')?? 1?個(gè)內(nèi)部節(jié)點(diǎn)。

因?yàn)?v'?有 h(v') > 0 所以它是個(gè)內(nèi)部節(jié)點(diǎn)。同樣的它有黑色高度要么是 bh(v') 要么是 bh(v')-1 (依據(jù)v'是紅色還是黑色)的兩個(gè)兒子。通過歸納假設(shè)每個(gè)兒子都有至少?2bh(v') ? 1?? 1?個(gè)內(nèi)部接點(diǎn),所以?v'?有:

2bh(v') ? 1?? 1 + 2bh(v') ? 1?? 1 + 1 = 2bh(v')?? 1

個(gè)內(nèi)部節(jié)點(diǎn)。

使用這個(gè)引理我們現(xiàn)在可以展示出樹的高度是對(duì)數(shù)性的。因?yàn)樵趶母饺~子的任何路徑上至少有一半的節(jié)點(diǎn)是黑色(根據(jù)紅黑樹屬性4),根的黑色高度至少是h(root)/2。通過引理我們得到:

因此根的高度是O(log(n))。

轉(zhuǎn)載于:https://www.cnblogs.com/madonion/articles/2271842.html

總結(jié)

以上是生活随笔為你收集整理的[wiki]红黑树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。