1. 程式人生 > >演算法導論 第十二章:二叉查詢樹 筆記(二叉查詢樹、查詢二叉查詢樹、插入和刪除、隨機構造的二叉查詢樹)

演算法導論 第十二章:二叉查詢樹 筆記(二叉查詢樹、查詢二叉查詢樹、插入和刪除、隨機構造的二叉查詢樹)

二叉查詢樹是一種樹資料結構,它與普通的二叉樹最大的不同就是二叉查詢樹滿足一個性質:對於樹中的任意一個節點,均有其左子樹中的所有節點的關鍵字值都不大於該節點的關鍵字值,其右子樹中的任意一個節點的關鍵字值都不小於該節點的關鍵字值。

在二叉查詢樹上可以進行搜尋、取最小值、取最大值、取指定節點的前驅、取指定節點的後繼以及插入和刪除節點操作,因此二叉查詢樹和堆(大頂堆和小頂堆)一樣,也可以做優先佇列,都能夠在 O(lgn) 的時間內取得集合的最大值和最小值。

一個二叉查詢樹的期望高度為O(lgn),因此在二叉查詢樹上的基本操作都能在期望時間O(lgn)下實現,但是最壞情況下時間為O(n),也就是二叉排序樹極度不平衡,變成一條鏈。對二叉查詢樹的前序、中序和後序遍歷均是在O(n) 的時間複雜度內實現。根據二叉查詢樹的性質,其中序遍歷的結果就是樹元素的按非遞減序輸出,因此研究二叉排序樹的中序遍歷及遍歷中元素的前驅和後繼是非常重要的。

二叉查詢樹:

對一顆二叉查詢樹的任何節點,該節點的左子樹中的任何一個節點的值都小於等於該節點的值,該節點的右子樹中的任何一個節點的值都大於等於該節點的值。

中序遍歷二叉查詢樹,輸出的序列是按序的。遍歷操作的複雜度O(n)。

如:

根據二叉查詢樹的性質,可以用一個遞迴演算法按排列順序輸出樹中的所有關鍵字。這種演算法稱為中序遍歷演算法,因為一子樹根的關鍵字在輸出時介於左子樹和右子樹的關鍵字之間(類似地,前序遍歷中根的關鍵字在其左右子樹中的關鍵字之前輸出,而後序遍歷中根的關鍵字在其左右子樹中的關鍵字之後輸出)。

虛擬碼:

INORDER-TREE-WALK(x)
    if x!=NIL
        then INORDER-TREE-WALK(left[x])
        print key[x]
        INORDER-TREE-WALK(right[x])

查詢二叉查詢樹:

對於二叉查詢樹,最常見的操作是查詢樹中的某個關鍵字。除了SEARCH 操作外, 二叉查詢樹還能支援諸如MINIMUM、MAXIMUM 、SUCCESSOR 和PREDECESSOR 等查詢。

查詢:

在二叉查詢樹中查詢一個給定的關鍵字k的過程與二分查詢很類似,根據二叉查詢樹在的關鍵字存放的特徵,很容易得出查詢過程:首先是關鍵字k與樹根的關鍵字進行比較,如果k大比根的關鍵字大,則在根的右子樹中查詢,否則在根的左子樹中查詢,重複此過程,直到找到與遇到空結點為止。

如:

查詢最大關鍵字和最小關鍵字:

根據二叉查詢樹的特徵,很容易查找出最大和最小關鍵字。查詢二叉樹中的最小關鍵字:從根結點開始,沿著各個節點的left指標查詢下去,直到遇到NULL時結束。如果一個結點x無左子樹,則以x為根的子樹中,最小關鍵字就是key[x]。查詢二叉樹中的最大關鍵字:從根結點開始,沿著各個結點的right指標查詢下去,直到遇到NULL時結束。

前驅和後繼:

查詢前驅步驟:先判斷x是否有左子樹,如果有則在left[x]中查詢關鍵字最大的結點,即是x的前驅。如果沒有左子樹,則從x繼續向上執行此操作,直到遇到某個結點是其父節點的右孩子結點。

如:

查詢後繼步驟:先判斷x是否有右子樹,如果有則在right[x]中查詢關鍵字最小的結點,即使x的後繼。如果沒有右子樹,則從x的父節點開始向上查詢,直到遇到某個結點是其父結點的左兒子的結點時為止。

如:

虛擬碼:

TREE-SEARCH(x,k)
    if x=NIL or k=key[x]
        then return x
    if k<key[x]
        then return TREE-SEARCH(left[x],k)
        else return TREE-SEARCH(right[x],k)
//或也可以這麼寫
ITERATIVE-TREE-SEARCH(x,k)
    while x!=NIL and k!=key[x]
        do if k < key[x]
            then x <- left[x]
            else x <- right[x]
    return x
TREE-MINIMUM(x)
    while left[x]!=NIL
        do x <- left[x]
    return key[x]
TREE-MAXIMUM(x)
    while right[x]!=NIL
        do x <- right[x]
    return x
TREE-SUCCESSOR(x)
    if right[x]!=NIL
        return TREE-MINIMUM(right[x])
    y <- p[x]
    while y!=NIL and x=right[y]
        do x <- y
           y <- p[x]
    return y

插入和刪除:

插入結點的位置對應著查詢過程中查詢不成功時候的結點位置,因此需要從根結點開始查詢帶插入結點位置,找到位置後插入即可。插入過程執行時間為O(h),h為樹的高度。

如:

從二叉查詢樹中刪除給定的結點z,分三種情況討論:

1、結點z沒有左右子樹,則修改其父節點p[z],使其為NULL。

如:

2、如果結點z只有一個子樹(左子樹或者右子樹),通過在其子結點與父節點建立一條鏈來刪除z。

如:

3、如果z有兩個子女,則先刪除z的後繼y(y沒有左孩子),在用y的內容來替代z的內容。

如:

對高度為h的二叉查詢樹,動態集合操作INSERT和DELETE的執行時間為O(h)。

虛擬碼:

TREE­-INSERT(T,z)
    y <- NIL
    x <- root[T]
    while x!=NIL
        do y <- x
            if key[z] < key[x]
                then x <- left[x]
                else x <- right[x]

    p[z] <- y
    if y=NIL
        then root[T] <- z
        else if key[z] < key[y]
            then left[y] <- z
            else right[y] <- z

TREE­-DELETE(T,z)
    if left[z]=NIL or right[z]=NIL
        then y <- z
        else y <- TREE-SUCCESSOR(T,z)
    if left[y]!=NIL
        then x <- left[y]
        else x <- right[y]
    if x!=NIL
        then p[x] <- p[y]
    if p[y]=NIL
        then root[T] <- x
        else if y=left[p[y]]
            then left[p[y]] <- x
            else right[p[y]] <- x
    if y!=z
        then key[z] <- key[y]
    return y

隨機構造的二叉查詢樹:

我們可以定義在n個不同的關鍵字上的一棵隨機構造的二叉查詢樹,它是通過按隨機的順序,將各關鍵字插入一棵初始為空的樹而形成的,並且各輸入關鍵字的n!種排列是等可能的。一棵隨機構造二叉查詢樹的期望高度為O(lgn),從而基本動態集合的操作平均時間為θ(lgn)。 假設所有的關鍵字都是不同的。

先定義三個隨機變數:

Xnn個結點的二叉查詢樹的高度;

Yn:指數高度Yn=2的Xn次方Yn = 2*max(Yi-1,Yn-i)

Rn:根節點的在關鍵字中的統計順序。

Zn,i:定義指示器隨機變數Zn,i=I{Rn=i}; E[Zn,i] = 1/n

推理過程:

利用恆等式:

用數學歸納得出

再利用jensen不等式:

可得E[Xn] = O(lgn)。