跟我一塊兒學算法——紅黑樹

定義

紅黑樹(red-black tree)是一種改進的二叉查找樹,結點域中增長顏色屬性(紅或黑)。
二叉查找樹的通常操做的時間爲 O(lgn)。但若它退化成一棵n個結點的線性鏈後,操做最壞時間爲
O(n)。而紅黑樹增長了着色和相關性質保證了查找、插入、刪除的時間複雜度最壞爲 O(lgn).java

特徵算法

  1. 每一個節點非紅即黑
  2. 根結點爲黑
  3. 葉節點爲黑
  4. 紅色結點的孩子都爲黑色結點
  5. 從任意結點到其子樹中每一個葉子節點的路徑上包含相同數量的黑色結點
    在這裏插入圖片描述

紅黑樹和234樹是一種等價的數據結構數據結構

性質

  1. n個內結點的紅黑樹的高度最多爲2lg(n+1)
  2. 黑高度爲從某個結點出發(不包括該結點,包含葉子)到達一個葉結點的任意一 條路徑上,黑色結點的個數,
    記爲bh(x)。
  3. 黑高度爲k的紅黑樹,總結點數最多2(2k+1)-1個,內結點最多2(2k)-1(滿二叉,紅黑交替)。
    內部結點數最少2^k-1(滿二叉,全黑)。
  4. 某結點x到其後代結點的全部簡單路徑中,最長路徑最可能是最短路徑的2倍。
    參考https://blog.csdn.net/lanchunhui/article/details/75905478

旋轉

紅黑樹上的search、minimum、maximum、successor、predecessor 能夠在 O(logn)內完成。
時間複雜度O(1)
在這裏插入圖片描述性能

left-rotate(T,x)
  y=right[x]
  right[x]=left[x]
  p[left[y]]=x
  p[y]=p[x]
  if p[x]=nil
    root[T]=y
  else if x=left[p[x]]
    left[p[x]]Åy
    else right[p[x]]=y
  left[y]=x
  p[x]=y

jdk8 TreeMap右旋源代碼ui

/** From CLR */
    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;
        }
    }

插入

主要分兩步:.net

  • 首先和二叉查找樹的插入同樣,查找、插入。
  • 而後調整結構,保證知足紅黑樹狀態,對結點進行從新着色,以及對樹進行相關的旋轉操做。

二叉查找樹的就是一個二分查找,找到合適的位置就放進去。紅黑樹的插入在二叉查找樹插入的基礎上,爲了從新恢復平衡,繼續作了插入修復操做。設計

當咱們往紅黑樹中插入一個黑色節點時,會打破一個平衡:任一節點到它子樹的每一個葉子節點的路徑中都包含一樣數量的黑節點。
當咱們給一個紅色節點下插入一個紅色節點時,會打破另外一個平衡:紅色節點的左右孩子必定都是黑色節點。
爲了簡化調整,咱們直接把插入的節點直接染成紅色,這樣就不會影響上一個平衡,只要專心調整知足後一個平衡就行了。染成紅色後,咱們只要關心父節點是否爲紅,若是是紅的,就要把父節點進行變化,讓父節點變成黑色,或者換一個黑色節點當父親,這些操做的同時不能影響 不一樣路徑上的黑色節點數一致的規則。指針

關注插入節點的父親節點的位置,而父親節點位於其爺爺節點地左子樹或者右子樹的操做是相對稱的,座椅只須要研究一側,即插入位置的父親節點爲左子樹。code

插入、染紅後的調整有 2 種狀況:blog

狀況1.父親節點和叔叔節點都是紅色
在這裏插入圖片描述
如上圖所示,假設插入的是節點 N,這時父親節點 P 和叔叔節點 U 都是紅色,爺爺節點 G 必定是黑色。
紅色節點的孩子不能是紅色,這時無論 N 是 P 的左孩子仍是右孩子,只要同時把 P 和 U 染成黑色,G 染成紅色便可。這樣這個子樹左右兩邊黑色個數一致,也知足特徵 4。
可是這樣改變後 G 染成紅色,G 的父親若是是紅色可能又違反特徵 4 了,所以須要以 爺爺節點 G 爲新的調整節點,再次進行循環調整操做,直到父親節點不是紅的。

狀況2.父親節點爲紅色,叔叔節點爲黑色
在這裏插入圖片描述
如上圖所示,假設插入的是節點 N,這時父親節點 P 是紅色,叔叔節點 U 是黑色,爺爺節點 G 必定是黑色。
紅色節點的孩子不能是紅色,可是直接把父親節點 P 塗成黑色也不行,這條路徑多了個黑色節點。
經過把 爺爺節點 G 右旋,P 變成了這個子樹的根節點,G 變成了 P 的右子樹。
右旋後 G 跑到了右子樹上,這時把 P 變成黑的,多了一個黑節點,再把 G 變成紅的,就平衡了!

上面講的是插入節點 N 在父親節點 P 的左孩子位置,若是 N 是 P 的右孩子,就須要多進行一次左旋,把狀況化解成上述狀況,以下圖:
在這裏插入圖片描述
時間複雜度:O(lgn),最多兩次旋轉
4. 刪除
O(lgn)最多三次旋轉
5. 查找
O(lgn)

數據結構擴張

  1. 挑選一個合適的基本數據結構
  2. 決定在基本數據結構上增長的信息
  3. 修改基本數據結構上的操做並維持原有的性能
  4. 修改或設計新的操做

擴展結構

  1. 序統計樹
    序統計就是在一系列數中找出最大、最小值,某個數的序值等操做。
    結點域增長size(以x爲根的子樹所包含的內部結點數,包括x)
    操做的時間複雜度O(lgn)

  2. 區間樹O(lgn)

應用場景

  • 普遍用於C++的STL中,map和set都是用紅黑樹實現的.
  • 著名的Linux進程調度Completely Fair Scheduler,用紅黑樹管理進程控制塊,進程的虛擬內存區域都存儲在一顆紅黑樹上,每一個虛擬地址區域都對應紅黑樹的一個節點,左指針指向相鄰的地址虛擬存儲區域,右指針指向相鄰的高地址虛擬地址空間.
  • IO多路複用epoll的實現採用紅黑樹組織管理sockfd,以支持快速的增刪改查.
  • Ngnix中,用紅黑樹管理timer,由於紅黑樹是有序的,能夠很快的獲得距離當前最小的定時器.
  • Java中TreeMap的實現.

參考

《算法導論》
https://blog.csdn.net/lanchunhui/article/details/75905478
https://blog.csdn.net/u011240877/article/details/53329023
http://www.javashuo.com/article/p-vrluzavz-ez.html

相關文章
相關標籤/搜索