數據結構與算法_25 _ 紅黑樹(上):爲何工程中都用紅黑樹這種二叉樹

上兩節,咱們依次講了樹、二叉樹、二叉查找樹。二叉查找樹是最經常使用的一種二叉樹,它支持快速插入、刪除、查找操做,各個操做的時間複雜度跟樹的高度成正比,理想狀況下,時間複雜度是O(logn)。算法

不過,二叉查找樹在頻繁的動態更新過程當中,可能會出現樹的高度遠大於log2n的狀況,從而致使各個操做的效率降低。極端狀況下,二叉樹會退化爲鏈表,時間複雜度會退化到O(n)。我上一節說了,要解決這個複雜度退化的問題,咱們須要設計一種平衡二叉查找樹,也就是今天要講的這種數據結構。數據結構

不少書籍裏,但凡講到平衡二叉查找樹,就會拿紅黑樹做爲例子。不只如此,若是你有必定的開發經驗,你會發現,在工程中,不少用到平衡二叉查找樹的地方都會用紅黑樹。你有沒有想過,爲何工程中都喜歡用紅黑樹,而不是其餘平衡二叉查找樹呢?數據結構和算法

帶着這個問題,讓咱們一塊兒來學習今天的內容吧!性能

什麼是「平衡二叉查找樹」?

平衡二叉樹的嚴格定義是這樣的:二叉樹中任意一個節點的左右子樹的高度相差不能大於1。從這個定義來看,上一節咱們講的徹底二叉樹、滿二叉樹其實都是平衡二叉樹,可是非徹底二叉樹也有多是平衡二叉樹。學習

平衡二叉查找樹不只知足上面平衡二叉樹的定義,還知足二叉查找樹的特色。最早被髮明的平衡二叉查找樹是AVL樹,它嚴格符合我剛講到的平衡二叉查找樹的定義,即任何節點的左右子樹高度相差不超過1,是一種高度平衡的二叉查找樹。spa

可是不少平衡二叉查找樹其實並無嚴格符合上面的定義(樹中任意一個節點的左右子樹的高度相差不能大於1),好比咱們下面要講的紅黑樹,它從根節點到各個葉子節點的最長路徑,有可能會比最短路徑大一倍。設計

咱們學習數據結構和算法是爲了應用到實際的開發中的,因此,我以爲沒必去死摳定義。對於平衡二叉查找樹這個概念,我以爲咱們要從這個數據結構的由來,去理解「平衡」的意思。blog

發明平衡二叉查找樹這類數據結構的初衷是,解決普通二叉查找樹在頻繁的插入、刪除等動態更新的狀況下,出現時間複雜度退化的問題。ip

因此,平衡二叉查找樹中「平衡」的意思,其實就是讓整棵樹左右看起來比較「對稱」、比較「平衡」,不要出現左子樹很高、右子樹很矮的狀況。這樣就能讓整棵樹的高度相對來講低一些,相應的插入、刪除、查找等操做的效率高一些。開發

因此,若是咱們如今設計一個新的平衡二叉查找樹,只要樹的高度不比log2n大不少(好比樹的高度仍然是對數量級的),儘管它不符合咱們前面講的嚴格的平衡二叉查找樹的定義,但咱們仍然能夠說,這是一個合格的平衡二叉查找樹。

如何定義一棵「紅黑樹」?

平衡二叉查找樹其實有不少,好比,Splay Tree(伸展樹)、Treap(樹堆)等,可是咱們提到平衡二叉查找樹,聽到的基本都是紅黑樹。它的出鏡率甚至要高於「平衡二叉查找樹」這幾個字,有時候,咱們甚至默認平衡二叉查找樹就是紅黑樹,那咱們如今就來看看這個「明星樹」。

紅黑樹的英文是「Red-Black Tree」,簡稱R-B Tree。它是一種不嚴格的平衡二叉查找樹,我前面說了,它的定義是不嚴格符合平衡二叉查找樹的定義的。那紅黑樹到底是怎麼定義的呢?

顧名思義,紅黑樹中的節點,一類被標記爲黑色,一類被標記爲紅色。除此以外,一棵紅黑樹還須要知足這樣幾個要求:

  • 根節點是黑色的;

  • 每一個葉子節點都是黑色的空節點(NIL),也就是說,葉子節點不存儲數據;

  • 任何相鄰的節點都不能同時爲紅色,也就是說,紅色節點是被黑色節點隔開的;

  • 每一個節點,從該節點到達其可達葉子節點的全部路徑,都包含相同數目的黑色節點;

這裏的第二點要求「葉子節點都是黑色的空節點」,稍微有些奇怪,它主要是爲了簡化紅黑樹的代碼實現而設置的,下一節咱們講紅黑樹的實現的時候會講到。這節咱們暫時不考慮這一點,因此,在畫圖和講解的時候,我將黑色的、空的葉子節點都省略掉了。

爲了讓你更好地理解上面的定義,我畫了兩個紅黑樹的圖例,你能夠對照着看下。

爲何說紅黑樹是「近似平衡」的?

咱們前面也講到,平衡二叉查找樹的初衷,是爲了解決二叉查找樹由於動態更新致使的性能退化問題。因此,「平衡」的意思能夠等價爲性能不退化。「近似平衡」就等價爲性能不會退化得太嚴重

咱們在上一節講過,二叉查找樹不少操做的性能都跟樹的高度成正比。一棵極其平衡的二叉樹(滿二叉樹或徹底二叉樹)的高度大約是log2n,因此若是要證實紅黑樹是近似平衡的,咱們只須要分析,紅黑樹的高度是否比較穩定地趨近log2n就行了。

紅黑樹的高度不是很好分析,我帶你一步一步來推導。

首先,咱們來看,若是咱們將紅色節點從紅黑樹中去掉,那單純包含黑色節點的紅黑樹的高度是多少呢?

紅色節點刪除以後,有些節點就沒有父節點了,它們會直接拿這些節點的祖父節點(父節點的父節點)做爲父節點。因此,以前的二叉樹就變成了四叉樹。

前面紅黑樹的定義裏有這麼一條:從任意節點到可達的葉子節點的每一個路徑包含相同數目的黑色節點。咱們從四叉樹中取出某些節點,放到葉節點位置,四叉樹就變成了徹底二叉樹。因此,僅包含黑色節點的四叉樹的高度,比包含相同節點個數的徹底二叉樹的高度還要小。

上一節咱們說,徹底二叉樹的高度近似log2n,這裏的四叉「黑樹」的高度要低於徹底二叉樹,因此去掉紅色節點的「黑樹」的高度也不會超過log2n。

咱們如今知道只包含黑色節點的「黑樹」的高度,那咱們如今把紅色節點加回去,高度會變成多少呢?

從上面我畫的紅黑樹的例子和定義看,在紅黑樹中,紅色節點不能相鄰,也就是說,有一個紅色節點就要至少有一個黑色節點,將它跟其餘紅色節點隔開。紅黑樹中包含最多黑色節點的路徑不會超過log2n,因此加入紅色節點以後,最長路徑不會超過2log2n,也就是說,紅黑樹的高度近似2log2n。

因此,紅黑樹的高度只比高度平衡的AVL樹的高度(log2n)僅僅大了一倍,在性能上,降低得並很少。這樣推導出來的結果不夠精確,實際上紅黑樹的性能更好。

解答開篇

咱們剛剛提到了不少平衡二叉查找樹,如今咱們就來看下,爲何在工程中你們都喜歡用紅黑樹這種平衡二叉查找樹?

咱們前面提到Treap、Splay Tree,絕大部分狀況下,它們操做的效率都很高,可是也沒法避免極端狀況下時間複雜度的退化。儘管這種狀況出現的機率不大,可是對於單次操做時間很是敏感的場景來講,它們並不適用。

AVL樹是一種高度平衡的二叉樹,因此查找的效率很是高,可是,有利就有弊,AVL樹爲了維持這種高度的平衡,就要付出更多的代價。每次插入、刪除都要作調整,就比較複雜、耗時。因此,對於有頻繁的插入、刪除操做的數據集合,使用AVL樹的代價就有點高了。

紅黑樹只是作到了近似平衡,並非嚴格的平衡,因此在維護平衡的成本上,要比AVL樹要低。

因此,紅黑樹的插入、刪除、查找各類操做性能都比較穩定。對於工程應用來講,要面對各類異常狀況,爲了支撐這種工業級的應用,咱們更傾向於這種性能穩定的平衡二叉查找樹。

內容小結

不少同窗都以爲紅黑樹很難,的確,它算是最難掌握的一種數據結構。其實紅黑樹最難的地方是它的實現,咱們今天尚未涉及,下一節我會專門來說。

不過呢,我認爲,咱們其實不該該把學習的側重點,放到它的實現上。那你可能要問了,關於紅黑樹,咱們究竟須要掌握哪些東西呢?

還記得我屢次說過的觀點嗎?咱們學習數據結構和算法,要學習它的由來、特性、適用的場景以及它能解決的問題。對於紅黑樹,也不例外。你若是能搞懂這幾個問題,其實就已經足夠了。

紅黑樹是一種平衡二叉查找樹。它是爲了解決普通二叉查找樹在數據更新的過程當中,複雜度退化的問題而產生的。紅黑樹的高度近似log2n,因此它是近似平衡,插入、刪除、查找操做的時間複雜度都是O(logn)。

由於紅黑樹是一種性能很是穩定的二叉查找樹,因此,在工程中,但凡是用到動態插入、刪除、查找數據的場景,均可以用到它。不過,它實現起來比較複雜,若是本身寫代碼實現,難度會有些高,這個時候,咱們其實更傾向用跳錶來替代它。

課後思考

動態數據結構支持動態的數據插入、刪除、查找操做,除了紅黑樹,咱們前面還學習過哪些呢?能對比一下各自的優點、劣勢,以及應用場景嗎?

歡迎留言和我分享,我會第一時間給你反饋。

相關文章
相關標籤/搜索