為什麼被減數減去減數等於被減數加上減數的反碼?補數怎麼來的?為什麼用補數?可溢位計數系統
紅黑樹
一、為什麼會出現紅黑樹
因為一棵由n個結點隨機構造的二叉查詢樹的高度為lgn,所以順理成章,二叉查詢樹的一般操作的執行時間為O(lgn)。但二叉查詢樹若退化成了一棵具有n個結點的線性鏈後,則這些操作最壞情況執行時間為O(n)。
於是出現了平衡二叉樹,可以看我之前寫的部落格:
平衡二叉樹保證了在最差的情況下,二叉樹依然能夠保持絕對的平衡,即左右兩個子樹的高度差的絕對值不超過1。但是這又會帶來一個問題,那就是平衡二叉樹的定義過於嚴格,導致每次插入或者刪除一個元素之後,都要去維護二叉樹整體的平衡,這樣產生額外的代價又太大了。二叉搜尋樹可能退化成連結串列,而平衡二叉樹維護平衡的代價開銷又太大了,那怎麼辦呢?這就要談到“中庸之道”的智慧了。說白了就是把平衡的定義適當放寬,不那麼嚴格,這樣二叉樹既不會退化成連結串列,維護平衡的開銷也可以接受。沒錯,這就是我們要談的紅黑樹了。
二、什麼是紅黑樹
紅黑樹是一種含有紅黑結點並能自平衡的二叉查詢樹。它必須除了滿足二叉搜尋樹的性質外,還要滿足下面的性質:
- 每個節點要麼是黑色,要麼是紅色。
- 根節點是黑色。
- 每個葉子節點是黑色。
- 每個紅色結點的兩個子結點一定都是黑色。
- 任意一結點到每個葉子結點的路徑都包含數量相同的黑結點。
正是紅黑樹的這5條性質,使一棵n個結點的紅黑樹始終保持了logn的高度,從而也就解釋了上面所說的“紅黑樹的查詢、插入、刪除的時間複雜度最壞為O(log n)”這一結論成立的原因。
三、插入的過程
1、按照搜尋樹的特徵進行插入
2、插入的時候有意識的碰瓷是紅色不能相鄰這個特徵(即插入的為 紅色節點)
3、隨著插入的完成
- 插入的節點是根節點,把顏色改為黑色
- 插入的節點的父節點是黑節點,插入完成
- 插入接節點的父節點為紅色,破壞了紅色節點不能相鄰的原則,需要修復
4、插入完成,結果也是一顆紅黑樹
四、破壞怎麼修復
已知條件:新插入節點(cur)是紅色。cur 的父節點(parent)是紅色
推匯出結論:cur 的祖父節點-- 父親的父親(grandpa)一定是黑色
因為,如果grandpa 為紅色,則parent 和 grandpa 都是紅色,已經不再是紅黑樹了。
1、情況一
1、uncle是存在的,且顏色為紅色,即為下圖:
grandpa + parent/uncle 代表的是兩條路徑,在這兩條路徑上,各自提供了1個黑色
修復規則:任然保證這兩條路徑上只有一個黑色
即:grandpa:黑 -> 紅
parent/uncle:紅 -> 黑
然後再考慮 grandpa 和它的父節點問題
cur = grandpa
parent = cur.parent
grandpa = parent.parent
按照同樣的規則進行檢查,直到 根 or 滿足條件
修改後的樣子:
2、情況2
情況2:uncle 不存在或者 uncle 存在,顏色為黑色
情況2.1、cur 是 parent 左邊,並且 parent 是 grandpa 左邊(兩個順邊)
修復方式:
對 parent 進行右旋
parent:紅 -> 黑
grandpa:黑 -> 紅
所有路徑只有一個黑色
四條路徑,黑色顏色不一樣多,所以 parent 之前一定右子樹,並且兩邊子樹至少有一層黑色
修復後:
情況2.2、cur 是 parent 右邊,並且 parent 是 grandpa 右邊(兩個順邊)
修復方式:
對 parent 進行左旋
parent:紅 -> 黑
grandpa:黑 -> 紅
情況2.3、cur 是 parent 右邊,並且 parent 是 grandpa 左邊(不順邊)
修復方法:
對 parent 進行 右旋
原來的cur 視為 parent
原來的parent 視為 cur
得到如下:
然後再按照2.1處理
情況2.4、cur 是 parent 左邊,並且 parent 是 grandpa 右邊(不順邊)
同2.3
最後,把根節點同一改為 黑色
五、用途
紅黑樹 和 AVL 樹適用範圍是重疊的,都是 二叉平衡搜尋樹,都適合記憶體級別的資料查詢問題
用途:
jdk/c++/linux 中都比較偏向使用紅黑樹
Windows 偏向使用 AVL樹
Java 中使用紅黑樹的地方
1、TreeMap/TreeSet
2、HashMap如果發生衝突太嚴重,用紅黑樹代替連結串列