不知道你們有沒有看今天的那個面試官被害視頻,我那神奇的同事不知道那個腦回路忽然被打通了,在辦公室問了一句:是否是面試官問了一下紅黑樹,把面試的人給問毛了啊,都問,我不會這個還問,而後暴起下手呀!!而後辦公室掀起了一陣討論熱潮面試
樹,這個大學時代數據結構與算法的重點之一,當時真的也是頭疼了很久,可是其實如今想一想,害,沒啥變化,依舊頭疼,看下面這張圖,樹包含的內容算法
而樹的內容又以二叉樹做爲重點,先來複習一下基礎知識數據結構
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架構師聯盟