算法導論筆記之二叉搜索樹

12.一、二叉搜索樹的定義
一、一顆二叉搜索樹是以二叉樹來組織的,它具備以下性質:
設x是二叉搜索樹中的一個節點,若是y是x左子樹中的一個節點,那麼y.key <= x.key;若是y是x右子樹中的一個節點,那麼y.key >= x.key。咱們用left、right、p分別表示二叉搜索樹的左子樹、右子樹和父節點。
二、根據二叉搜索樹的性質,對二叉搜索樹進行中序遍歷,便可獲得一個遞增的序列;也就是說,爲了檢查一個樹是不是二叉搜索樹,可使用中序遍歷。
中序遍歷的僞代碼爲(遞歸):
InorderTreeWalk(x)
if(x != NIL)
InorderTreeWalk(x.left);
print x.key;
InorderTreeWalk(x.right);
練習:
12.1-2:
二叉搜索樹:左子樹關鍵字<根結點關鍵字<右子樹關鍵字
最小堆:根結點關鍵字 <= 左子樹關鍵字 && 根結點關鍵字 <= 右子樹關鍵字
不能,由於在最小堆中,左子樹和右子樹的大小關係不能肯定,而若是經過比較算法來進行排序的話,其下界爲n*lgn 。
12.1-3:
用棧實現:見算法導論-10.4-有根樹的表示中的10.4-3
不用棧實現:見算法導論-10.4-5
12.1-4:
和中序遍歷相似。只不過遍歷根節點的順序不一樣,其僞代碼以下:
PreorderTreeWalk(x)
if(x != NIL)
print x.key;
InorderTreeWalk(x.left);
InorderTreeWalk(x.right);
PostorderTreeWalk(x)
if(x != NIL)
InorderTreeWalk(x.left);
InorderTreeWalk(x.right);
print x.key;
12.1-5:
反證法。假設存在一種基於比較的算法,從n個元素的任意序列中構造一顆二叉搜索樹,其最壞狀況下的時間複雜度Ω低於n*lgn。則能夠構造一種基於比較的排序模型,首先,構造一顆二叉搜索樹(時間複雜度爲Ω),而後以中序遍歷輸出該二叉搜索樹的元素(時間複雜度爲n),便可完成排序。最終,該算法的時間複雜度爲max(Ω,n),小於n*lgn,這與題設中全部基於比較的排序模型的算法的時間複雜度的下界爲n*lgn矛盾,所以假設不成立,原命題得證。
12.二、查詢二叉搜索樹
一、查找。
二叉搜索樹查找操做的僞代碼爲(遞歸):
TreeSearch(x,k)
if(x == NIL || x .key == k)
return x;
if(k < x.key)
return TreeSearch(x.left,k);
else
return TreeSearch(x.right,k);
二叉搜索樹查找操做的僞代碼爲(循環):
TreeSearch(x)
while(x != NIL || x .key != k)
if(k < x.key)
x = x.left;
else
x = x.right;
return x;
二、最大和最小關鍵字元素
根據二叉搜索樹的性質:經過從樹根開始沿着left孩子的指針直到遇到NIL,咱們總能在一顆二叉搜索樹中找到一個元素,該元素就是以x爲根的二叉搜索樹的最小關鍵字元素;同理,從樹根開始沿着right孩子的指針直到遇到NIL,便可得到最大關鍵字元素。
得到最小關鍵字元素的僞代碼爲(循環):
TreeMinimum(x)
while(x.left != NIL)
x = x.left;
return x;
得到最小關鍵字元素的僞代碼爲(遞歸):
TreeMinimum(x)
if(x.left == NIL)
return x;
return TreeMinimum(x.left);
獲取最大關鍵字元素和獲取最小關鍵字元素的僞代碼是對稱的,爲:
循環:
TreeMaximum(x)
while(x.right != NIL)
x = x.right;
return x;
遞歸:
TreeMaximum(x)
if(x.right == NIL)
return x;
return TreeMaximum(x.right);
三、前驅和後繼
給定一個二叉搜索樹中的一個節點,有時須要按中序遍歷查找它的前驅和後繼。若是全部關鍵字互不相同,則一個節點x的後繼是大於x.key的最小關鍵字節點,x的前驅是小於x.key的最大關鍵字節點。若是x就是該二叉搜索樹的最小或最大關鍵字,則x的前驅或後繼爲NIL。

獲取二叉搜索樹後繼的僞代碼爲:
TreeSuccessor(x)
if(x.right != NIL)
return TreeMinimum(x.right);
y = x.p;
while( y != NIL && x == y.right)
x = y;
y = y.p;
return y;
把TreeSuccessor分爲兩種狀況:若是x的右子樹非空,那麼x的後繼恰是x右子樹中最左的節點,經過TreeMinimum(x.right)能夠找到;另外一方面,若是x的右子樹爲空而且有一個後繼y,那麼y就是x的有左孩子的最底層祖先。

獲取二叉搜索樹前驅與獲取二叉搜索樹後繼的行爲是對稱的,其僞代碼爲:
TreePredecessor(x)
if(x.left != NIL)
return TreeMaximum(x.left);
y = x.p;
while(y != NIL && x == y.left)
x= y;
y = y.p;
return y;

練習:
12.2-1:
根據二叉搜索樹查找的特色,遇到比363小的數,就往右找,查找序列中的數會愈來愈大;反之,遇到比363大的數,就往左找,查找序列中的數就愈來愈小。所以,比363大的數是按降序排列的,比363小的數是按升序排列的。因此c和e不是查找過的序列。
12.2-二、12.2-3:見前面的僞代碼。
12.2-4 反例太多了...
12.2-5:
若是二叉搜索樹有右孩子,則它的後繼爲TreeMinimum(x.right),爲最左邊的元素,沒有左孩子;同理可知,它的前驅沒有右孩子。

12.3 插入和刪除
一、插入
要將一個新值v插入到一顆二叉搜索樹T中,須要調用TreeInsert。該過程以節點z做爲輸入,其中z.key = v,z.left = NIL,z.right = NIL。插入過程的僞代碼爲:
TreeInsert(T,Z)
y = NIL;
x = T.root;
while (x != NIL)
y = x;
if(z.key < x.key)
x = x.left;
else
x = x.right;
z.p = y;
if(y == NIL) //樹是空的
T.root = z;
else if(z.key < y.key)
y.left = z;
else
y.right = z;

二、刪除
從一顆二叉搜索樹T中刪除一個節點z分爲三種狀況:
a:若是z沒有孩子節點,那麼只須要簡單的將它刪除,並修改它的父節點,用NIL做爲孩子替換z便可。
b:若是z只有一個孩子節點,那麼將這個孩子提高到樹z的位置中,並修改z的父節點,用z的孩子來替換z
c:若是z有兩個孩子,那麼找到z的後繼y,並讓y佔據樹中z的位置。z原來的右子樹部分紅爲y的新的右子樹,z原來的左子樹成爲y的新的左子樹。
P167的圖12-4總結了刪除二叉搜索樹節點時的四種狀況。
從一顆二叉搜索樹T中刪除一個節點z的僞代碼爲:

首先定義一個在二叉搜索樹內遷移節點的函數Transplant.它是用一顆子樹v替換一顆子樹u,併成爲u的雙親的孩子節點。
Transplant(T,u,v)
//u是根節點
if(u.p == NIL)
T.root = v;
//u是其雙親的左孩子
else if(u == u.p.left)
u.p.left = v;
//u是其雙親的右孩子
else
u.p.right = v;
if(v != NIL)
v.p = u.p;

最後是TreeDelete的僞代碼:
TreeDelete(T,z)
if(z.left == NIL)
Transplant(T,z,z.right);//z.right爲NIL也能正常刪除
else if(z.right == NIL)
Transplant(T,z,z.left);
else
//找z的後繼,由於z有右子樹,因此直接調用TreeMinimum便可
y = TreeMinimum(z.right);
if(y.p != z)
Transplant(T,y,y.right);
y.right = z.right;
y.right.p = y;
Transplant(T,z,y);
y.left = z.left;
y.left.p = y;算法

相關文章
相關標籤/搜索