常見數據結構(二)-樹(二叉樹,紅黑樹,B樹)

本文介紹數據結構中幾種常見的樹:二分查找樹,2-3樹,紅黑樹,B樹

寫在前面

Binary Search Tree(二分查找樹)

定義:A BST is a binary tree in symmetric order.

A binary tree is either:

  • Empty.
  • Two disjoint binary trees (left and right).

Symmetric order.Each node has a key, and every node’s key is:

  • Larger than all keys in its left subtree.
  • Smaller than all keys in its right subtree.

在java的實現中,每個節點(Node)由四個域組成:key,value,left,right。即:鍵,值,左子樹,右子樹。

private class Node {
    private Key key;
    private Value val;
    private Node left, right;

    public Node(Key key, Value val) {
        this.key = key;
        this.val = val;
    }
}

Binary Search Tree

  • 查找:得到相應鍵的值,若無此鍵則返回null.
/* 查找 */
public Value get(Key key) {
    Node x = root;
    while (x != null) {
        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            x = x.left;
        } else if (cmp > 0) {
            x = x.right;
        } else { // if (cmp == 0)
          return x.val;
        }
    }
    return null;
}
  • 插入:如果小,往左;如果大,往右;如果null,插入;如果存在,覆蓋。
/* 插入 */
public void put(Key key, Value val) {
    root = put(root, key, val);
}

/* 輔助函數,遞歸調用 */
private Node put(Node x, Key key, Value val) {
    if (x == null) return new Node(key, val);
    int cmp = key.compareTo(x.key);
    if (cmp < 0) {
        x.left = put(x.left, key, val);
    } else if (cmp > 0) {
        x.right = put(x.right, key, val);
    } else { // if (cmp == 0)
        x.val = val;
    }
    return x;
}

比較的次數爲節點的深度+1,由於插入節點的順序會有差異,所以樹的高度不確定,最壞的情況是N個節點的樹高度爲N。

  • 刪除:列出下面幾種處理方法
    • 將值置爲null,在樹中保留鍵
    • 刪除最小值:一直向左找到左子樹爲null的節點,用它的右子節點代替它。
    • Hibbard deletion

下面重點講一下Hibbard deletion,分爲三種情況:

  1. 沒有子節點的節點,將其parent link置爲null即可。
  2. 有一個子節點的節點,刪除該節點並以子節點代替即可。
  3. 有兩個子節點的節點,找到該節點t的下一個節點x(即右子樹的最小節點),在右子樹刪除這個節點,並將該節點x放到t的位置。
/* 刪除 */
private Node delete(Node x, Key key) {
    if (x == null) return null;
    int cmp = key.compareTo(x.key);
    if (cmp < 0) {
        x.left = delete(x.left, key);
    } else if (cmp > 0) {
        x.right = delete(x.right, key);
    } else {
        if (x.right == null) return x.left; // no right child
        if (x.left == null) return x.right; // no left child
        Node t = x;
        x = min(t.right); // replace with successor
        x.right = deleteMin(t.right);
        x.left = t.left;
    }
    x.count = size(x.left) + size(x.right) + 1;
    return x;
}

2-3 Search Trees(2-3樹)

在介紹紅黑樹前,先介紹一下2-3樹,便於後面理解紅黑樹。

2-3樹是二分查找樹的變形,每個節點是下面兩種情況之一:

  • 2-node:一個鍵,兩個分叉(smaller,larger)
  • 3-node:兩個鍵,三個分叉(smaller,between,larger)

2-3 trees

在底部向一個3-node插入。

  • 向3-node插入一個鍵,臨時成爲一個4-node
  • 將4-node中間的key移動到父節點
  • 向上重複
  • 如果到了頂端的根節點,且根節點是4-node,將其分成3個2-nodes.

總結起來就是:當插入的值導致節點變四叉時進行分裂,將中間的值傳給上一個節點,並將另外兩個值作爲兩個子節點分開,若上一節點也因此變成四叉,依次類推。分裂4-node是一個local transformation,只會進行常數次數的操作。高度加一由且僅由頂節點分裂造成

2-3 trees proof

樹的高度,在查找和插入時,保證了logarithmic的性能。

  • Worst case: lg N. [all 2-nodes]
  • Best case: log3 N ≈ 0.631 lg N. [all 3-nodes]

Red-Black BSTs(紅黑樹)

這裏的紅黑樹均指Left-leaning red-black BSTs。主要是用二叉樹的形式來表示2-3樹,用一個“內部”的left-leaning連接來表示3-node。red link是2-3tree的三叉節點的連接兩個key的內部link,大值作爲根節點,小值作爲左子節點,故名left leaning 紅黑樹。

紅黑樹定義

一個等價的定義,A BST such that:

  • No node has two red links connected to it.
  • Every path from root to null link has the same number of black links.
  • Red links lean left.

紅黑樹對應2-3樹

紅黑樹的java表示

private static final boolean RED = true;
private static final boolean BLACK = false;

private class Node {
    Key key;
    Value val;
    Node left, right;
    boolean color;// color of parent link
}

private boolean isRed(Node x) {
    if (x == null) return false;
    return x.color == RED;
}

左轉-右轉-變色

紅黑樹插入過程中可能用到的三個基本操作(左轉,右轉,變色):

  • left rotate
  • right rotate
  • flip colors

下面依次介紹

  • 左轉

紅黑樹左轉

/* left rotate */
private Node rotateLeft(Node h) {
   assert isRed(h.right);
   Node x = h.right;
   h.right = x.left;
   x.left = h;
   x.color = h.color;
   h.color = RED;
   return x;
}
  • 右轉

紅黑樹右轉

/* right rotate */
private Node rotateRight(Node h) {
    assert isRed(h.left);
    Node x = h.left;
    h.left = x.right;
    x.right = h;
    x.color = h.color;
    h.color = RED;
    return x;
}
  • 變色

紅黑樹變色

/* flip colors */
private void flipColors(Node h) {
    assert !isRed(h);
    assert isRed(h.left);
    assert isRed(h.right);
    h.color = RED;
    h.left.color = BLACK;
    h.right.color = BLACK;
}

插入操作

紅黑樹插入兩個節點

從圖中可以看出,插入的次序不同,需要轉換的操作也不同,分三種情況(圖中每一列是一種情況):

  1. 已有a和b時,c插入在b的右子節點,直接變色即可
  2. 已有b和c時,a插入在b的左子節點,先右轉把b滑上去,成1中的狀態,再變色即可
  3. 已有a和c時,b插入在a的右子節點,先左轉把a滑下去,成2中的狀態,再右轉+變色即可

從上面的分析可以看出,三種情況之間有轉換關係,且逐步趨向簡單,如下圖所示:

紅黑樹狀態轉換

根本原因在於,2-3樹中,是把3-node中處於中間的那個鍵傳遞給父節點,所以在紅黑樹中,當有一個節點連了兩個 red link時,說明這三個點是一個3-node,但次序還需要調整,從而達到中間鍵在最上的狀態,進而變色。而這個這個調整的趨勢則是先讓b處於a,c中間(即a的父,c的左子,成一條線),再讓b成爲a,c的父節點,最後變色。記住這個順序和原因,寫代碼就簡單了,狀態3->狀態2->狀態1

private Node put(Node h, Key key, Value val) {
    //insert at bottom (and color it red)
    if (h == null) return new Node(key, val, RED);
    int cmp = key.compareTo(h.key);
    if (cmp < 0) {
        h.left = put(h.left, key, val);
    } else if (cmp > 0) {
        h.right = put(h.right, key, val);
    } else {
        h.val = val;
    }

    if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);// lean left
    if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);//balance 4-node
    if (isRed(h.left) && isRed(h.right)) flipColors(h);//split 4-node

    return h;
}

紅黑樹的高度 h <= 2 lg N,證明:

  • Every path from root to null link has same number of black links.
  • Never two red links in-a-row.

B-Trees(B樹)

最後簡單提一下B樹,就是將2-3樹一般化,將每個節點的key-link pairs增加到 M - 1

  • At least 2 key-link pairs at root.
  • At least M / 2 key-link pairs in other nodes.
  • External nodes contain client keys.
  • Internal nodes contain copies of keys to guide search.

B-Trees

在B樹中查找

  • Start at root.
  • Find interval for search key and take corresponding link.
  • Search terminates in external node.

在B樹中插入

  • Search for new key.
  • Insert at bottom.
  • Split nodes with M key-link pairs on the way up the tree.

命題:A search or an insertion in a B-tree of order M with N keys requires between log M-1 N and log M/2 N probes

發佈了8 篇原創文章 · 獲贊 18 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章