快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

不知道你們有沒有看今天的那個面試官被害視頻,我那神奇的同事不知道那個腦回路忽然被打通了,在辦公室問了一句:是否是面試官問了一下紅黑樹,把面試的人給問毛了啊,都問,我不會這個還問,而後暴起下手呀!!而後辦公室掀起了一陣討論熱潮面試

樹,這個大學時代數據結構與算法的重點之一,當時真的也是頭疼了很久,可是其實如今想一想,害,沒啥變化,依舊頭疼,看下面這張圖,樹包含的內容算法

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

而樹的內容又以二叉樹做爲重點,先來複習一下基礎知識數據結構

BST樹:架構

二叉搜索樹(Binary Search Tree,簡寫BST),又稱爲二叉排序樹,屬於樹的一種,經過二叉樹將數據組織起來,樹的每一個節點都包含了健值key、數據值data、左子節點指針、右子節點指針。其中健值key是最核心的部分,它的值決定了樹的組織形狀;數據值data是該節點對應的數據,有些場景能夠忽略,舉個例子,key爲身份證號而data爲人名,經過身份證號找人名;左子節點指針指向左子節點;右子節點指針指向右子節點。性能

特色:this

左右子樹也分別是二叉搜索樹。spa

左子樹的全部節點key值都小於它的根節點的key值。右子樹的全部節點key值都大於他的根節點的key值。二叉搜索樹能夠爲一棵空樹。3d

通常來講,樹中的每一個節點的 key值都不相等,但根據須要也能夠將相同的key值插入樹中指針

AVL樹:視頻

AVL樹,也稱平衡二叉搜索樹,AVL是其發明者姓名簡寫。AVL樹屬於樹的一種,並且它也是一棵二叉搜索樹,不一樣的是他經過必定機制能保證二叉搜索樹的平衡,平衡的二叉搜索樹的查詢效率更高。

特色:

AVL樹是一棵二叉搜索樹。

AVL樹的左右子節點也是AVL樹。

AVL樹擁有二叉搜索樹的全部基本特色。

每一個節點的左右子節點的高度之差的絕對值最多爲1,即平衡因子爲範圍爲[-1,1]。

還有一個就是今天咱們討論的重點:紅黑樹,咱們就來看一下

紅黑(Red-black)樹

是一種自平衡二叉查找樹,1972年由Rudolf Bayer發明,它與AVL樹相似,都在插入和刪除操做時能經過旋轉操做保持二叉查找樹的平衡,以便能得到高效的查找性能。它能夠在O(logn)時間內作查找,插入和刪除等操做。紅黑樹是2-3-4樹的一種等同,但有些紅黑樹設定只能左邊是紅樹,這種狀況就是2-3樹的一種等同了。對於AVL樹來講,紅黑樹犧牲了部分平衡性以換取插入/刪除操做時少許的旋轉操做,總體來講性能要優於AVL樹。

特色:

節點是紅色或黑色。根節點是黑色。

每一個葉節點(NIL節點)是黑色的。

每一個紅色節點的兩個子節點都爲黑色。(從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點)從任一節點到其每一個葉子的全部路徑都包含相同數目的黑色節點。

最長路徑不超過最短路徑的2倍

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

上圖就是一顆簡單的紅黑樹。其中Nil爲葉子結點,而且它是黑色的。(H和M的黑色葉子節點沒畫出來)

介紹到此,爲了後面講解不至於混淆,咱們還須要來約定下紅黑樹一些結點的叫法,如圖2所示。

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

咱們把正在處理(遍歷)的結點叫作當前結點,如圖2中的D,它的父親叫作父結點,它的父親的另一個子結點叫作兄弟結點,父親的父親叫作祖父結點。

3.基本操做

前面講到紅黑樹能自平衡,它靠的是什麼?三種操做:左旋、右旋和變色。

  • 左旋:逆時針旋轉,父節點被本身的右孩子取代,而本身成爲本身的左孩子(注:左旋隻影響旋轉結點和其右子樹的結構,把右子樹的結點往左子樹挪了。)

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

  • 右旋:順時針旋轉,父節點被左孩子取代,而本身成爲本身的右孩子(注:右旋隻影響旋轉結點和其左子樹的結構,把左子樹的結點往右子樹挪了。)

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

  • 變色:結點的顏色由紅變黑或由黑變紅。

因此不難看出,不管什麼旋轉操做都是局部的改變了樹的的節點,但要保持紅黑樹的性質,結點不能亂挪,還得靠變色了。怎麼變?具體情景有不一樣變法,來看一下

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

至此,變色的任務完成,按照步驟,知足規則,一步步進行,錯過一步可能就理解不了了

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

好了,理論的東西,到這裏基本就講解完了,紅黑樹難嗎?說實話我我的以爲,這破玩意太爲難人了

4.紅黑樹的部分實現

紅-黑樹的節點實現

紅-黑樹是對二叉搜索樹的改進,因此其節點與二叉搜索樹是差很少的,只不過在它基礎上增長了一個boolean型變量來表示節點的顏色,具體看RBNode類: 
public class RBNode<T extends Comparable<T>>{  
boolean color; //顏色    
T key; //關鍵字(鍵值)    
RBNode<T> left; //左子節點    
RBNode<T> right; //右子節點    
RBNode<T> parent; //父節點        
public RBNode(T key, boolean color, RBNode<T> parent, RBNode<T> left, RBNode<T> right) {  
this.key = key;       
this.color = color;       
this.parent = parent;        
this.left = left;        
this.right = right;    
}       
public T getKey() {       
return key;   
}       
public String toString() {       
return "" + key + (this.color == RED? "R" : "B");   
}
}

左旋的具體實現

上面對左旋的概念已經有了感性的認識了,這裏就再也不贅述了,咱們從下面的代碼中結合上面的示意圖,探討一下左旋的具體實現: /*************對紅黑樹節點x進行左旋操做 ******************//* * 左旋示意圖:對節點x進行左旋 
* 左旋作了三件事:
*  1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時) 
*   2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點爲y(左或右) 
*    3. 將y的左子節點設爲x,將x的父節點設爲y 
*    */
private void leftRotate(RBNode<T> x) {   
//1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時)    RBNode<T> y = x.right;    
x.right = y.left;        
if(y.left != null)        
y.left.parent = x;        
//2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點爲y(左或右)   
y.parent = x.parent;       
if(x.parent == null) {        
this.root = y; 
//若是x的父節點爲空,則將y設爲父節點    
} else {       
if(x == x.parent.left) 
//若是x是左子節點            
x.parent.left = y; 
//則也將y設爲左子節點        
else            
x.parent.right = y;
//不然將y設爲右子節點   
}        
//3. 將y的左子節點設爲x,將x的父節點設爲y   
y.left = x;    
x.parent = y;        
}

右旋具體實現

上面對右旋的概念已經有了感性的認識了,這裏也再也不贅述了,咱們從下面的代碼中結合上面的示意圖,探討一下右旋的具體實現: /*************對紅黑樹節點y進行右旋操做 ******************//* * 左旋示意圖:對節點y進行右旋 
* 右旋作了三件事: 
*  1. 將x的右子節點賦給y的左子節點,並將y賦給x右子節點的父節點(x右子節點非空時) 
*   2. 將y的父節點p(非空時)賦給x的父節點,同時更新p的子節點爲x(左或右) 
*    3. 將x的右子節點設爲y,將y的父節點設爲x 
*    */
private void rightRotate(RBNode<T> y) {  
//1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時)    RBNode<T> x = y.left;   
y.left = x.right;       
if(x.right != null)        
x.right.parent = y;        
//2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點爲y(左或右)    
x.parent = y.parent;       
if(y.parent == null) {       
this.root = x; 
//若是x的父節點爲空,則將y設爲父節點 
} else {      
if(y == y.parent.right) //若是x是左子節點          
y.parent.right = x; //則也將y設爲左子節點      
else          
y.parent.left = x;//不然將y設爲右子節點   
}       
//3. 將y的左子節點設爲x,將x的父節點設爲y   
x.right = y;  
y.parent = x;     
} 

5.功能

這裏我就簡單的介紹一下,篇幅緣由(我不會認可是我餓了,想去吃宵夜了,嘿嘿嘿),後面我會進行詳細的介紹

5.1查找

由於紅黑樹是一顆二叉平衡樹,而且查找不會破壞樹的平衡,因此查找跟二叉平衡樹的查找無異,爲了讓你們更好理解,看下面這張 二叉樹查找流程圖

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

很是簡單,但簡單不表明它效率很差。正因爲紅黑樹總保持黑色完美平衡,因此它的查找最壞時間複雜度爲O(2lgN),也即整棵樹恰好紅黑相隔的時候。能有這麼好的查找效率得益於紅黑樹自平衡的特性,而這背後的付出,紅黑樹的插入操做功不可沒~

5.2 插入

插入操做包括兩部分工做:

一查找插入的位置 即找到要插入的父節點; 二插入後自平衡。

查找插入的父結點很簡單,跟查找操做區別不大,仍是以一張流程圖總結一下

快進收藏吃灰!字節跳動大佬用最通俗方法講明白了紅黑樹算法

 

特別注意:

若是在面試的時候有人問你插入結點是應該是什麼顏色呢?答案是紅色。理由很簡單,紅色在父結點(若是存在)爲黑色結點時,紅黑樹的黑色平衡沒被破壞,不須要作自平衡操做。但若是插入結點是黑色,那麼插入位置所在的子樹黑色結點老是多1,必須作自平衡。

還有一個刪除,實在是太餓了,不行了,不能隨隨便便應付你們,聽我下回分解,哈哈哈哈,咱下次詳細的講解紅黑樹的應用

文章首發公衆號:Java架構師聯盟

相關文章
相關標籤/搜索