紅黑樹
紅黑樹 其實就是一個二叉樹。php
經常使用的二叉樹類型
簡單說二叉樹概念:二叉樹 又稱度爲至多二的樹。
平衡二叉樹微信
平衡二叉樹又稱 AVL 樹
特色:一個根節點的左右個子樹的高度差不超過1數據結構
平衡二叉樹app
非平衡二叉樹spa
高度差已經大於1 了。
平衡樹解決的問題就是 可以最大限度的增長訪問的每一個節點的的平均性
。保證每一個節點被訪問的次數平衡。.net
徹底二叉樹
除最後一層外,每一層上的結點數均達到最大值;在最後一層上只缺乏右邊的若干結點。
堆排序 結構其實就是一個徹底二叉樹的結構,倒序和正序就是用的 大根堆 小根堆的原理。3d
滿二叉樹
每一個節點是葉節點或者度爲2.
code
查找二叉樹
這種樹的特色是 每一個根節點大於左子樹上的任意一個節點,小於等於右子樹上的任意一個節點。
舉個簡單例子:
好比說 我如今要查找 4 這個數字 ,首先 我先比較 根節點就行,若是比根節點小的話,那麼確定在左邊的子樹列表裏面。那麼右邊的就不用看了。而後依次同理比較。
查找二叉樹 可以提升查詢速度的效率。orm
可是還有一種狀況 比較特殊:
這樣的 比較尷尬了,一邊倒的狀況它也知足查找二叉樹的概念。可是效率就不那麼高效了。blog
在說原理以前說一下
高度與深度區別:
1.深度定義是從上往下的
2.高度定義是從下往上的
3.空數高度0
4.葉子結點高度1
紅黑樹原理
知足一個樹是紅黑樹條件:
1.每一個節點要麼是紅色,要麼是黑色。
2.根節點必須是黑色
3.紅色節點不能連續(紅色節點的孩子和父親都不能都是紅色)
4.從任意節點出發,到其全部葉子節點的簡單路徑上都包含相同數目的黑色節點.
5.每一個紅色節點的兩個子節點必定都是黑色(葉子節點包含NULL)
紅黑樹的結構
如圖
紅黑樹從根節點到每一個葉子節點的路徑都包含相同數量的黑色節點,所以從根節點到葉子節點的路徑中包含的黑色節點數被稱爲樹的「黑色高度(black-height)
一顆樹黑色高度爲3得紅黑樹,從根結點到葉結點的最短路徑長度是3(黑-黑-黑),最長路徑爲4(黑-紅-黑-紅-黑)。因爲第4條性質,不可能在最長路徑中加入更多的黑色結點,由於性質3規定紅色結點的子結點必須是黑色的,所以在同一簡單路徑中不容許有兩個連續的紅色結點。紅黑樹中最長路徑就是一條紅黑交替的路徑
對於給定的黑色高度爲n的紅黑樹,從根結點到葉結點的簡單路徑的最短長度爲n-1,最大長度爲2(n-1)。全部對樹的操做必須保持上面列出的屬性。特別要指出的是,插入和刪除樹的結點的操做必須遵循這些原則。
紅黑樹插入過程當中狀況
每次插入元素的時候會將 元素 着色爲紅色。其目的爲了快的知足紅黑樹的4個條件
紅黑樹結構不會旋轉變化狀況:
1.當插入的節點爲的父親爲黑色節點。【什麼都不用作】
2.被插入的節點是根節點。【直接把此節點塗爲黑色】
紅黑樹結構發生旋轉變化狀況:
1. 當前節點的父節點【60】是紅色,且當前節點的祖父節點【40】的另外一個子節點(叔叔節點)也是紅色。
2.當前插入的父節點是紅色,當前叔叔節點的黑色,且當前節點爲其父親節點的左孩子。(進行左旋)
3.當前插入的父節點是紅色,當前叔叔節點的黑色,且當前節點爲其父親節點的右孩子。(進行右旋)
如圖所示
紅黑樹結構發生旋轉變化狀況已經對應的措施以下
左旋 :右邊過於臃腫
右旋 :左邊過於臃腫
相對複雜的紅黑樹 旋轉最大不超過3次
下面展現一個紅黑樹插入數據過程
樹的旋轉問題
爲何會出現旋轉?
對於平衡樹來講,當插入或者刪除的時候,樹的結構會發生破壞所以會致使。所以須要對樹進行旋轉來保證樹的平衡。
先拿 平衡二叉樹的 查找二叉樹舉一個例子:
此時當前二叉樹 是新增一個60數字紅色。此時 當前二叉樹不平衡了,那麼須要進行左旋 須要把當前40 那個節點做爲跟節點,而後把30和20 旋轉下來。
此時你們發現這樣仍是會有問題。發現又不知足二叉樹了,如今變三叉了,不要急 ,此時再次挑戰須要把中間的 33 那個分支砍掉,接在哪邊呢?根據查找二叉樹的規則,比根節點小的放在左邊,比根節點大的放在右邊。33 比40 小 可是 比30 大。如圖
紅黑樹應用
TreeMap 典型紅黑樹
TreeSet
左旋代碼:
右旋代碼:
插入元素:
//左旋右側須要平衡 private void rotateLeft(Entry<K,V> p) { if (p != null) { //拿到根節點的右子節點 Entry<K,V> r = p.right; //把根節點的右子節點的左節點,賦值 p.right = r.left; if (r.left != null) //將根節點這個值賦值到當前斷開的跟節點上 r.left.parent = p; //r 未來要成爲新的根節點 p.parent 爲根 ,使得他爲新的跟節點 r.parent = p.parent; if (p.parent == null) root = r; //若是p 爲左孩子,讓他仍是成爲左孩子 同理 else if (p.parent.left == p) p.parent.left = r; else p.parent.right = r; //最後 將當前交換的跟換值 r.left = p; p.parent = r; } }
private void rotateRight(Entry<K,V> p) { if (p != null) { Entry<K,V> l = p.left; p.left = l.right; if (l.right != null) l.right.parent = p; l.parent = p.parent; if (p.parent == null) root = l; else if (p.parent.right == p) p.parent.right = l; else p.parent.left = l; l.right = p; p.parent = l; } }
public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable super K> k = (Comparable super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; }
紅黑樹的優點
紅黑樹可以以O(log2(N))的時間複雜度進行搜索、插入、刪除操做。此外,任何不平衡都會在3次旋轉以內解決。這一點是AVL所不具有的,是很是高效的數據結構。
本文分享自微信公衆號 - WHICH工做室(which_cn)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。