《算法導論》學習之旅-第十二章-二叉搜索樹


序言

搜索樹數據結構支持許多動態集合操作,包括:SEARCH, MINIMUM, MAXIMUM, INSERT, DELETE, SUCCESSOR等。因此一個搜索樹既可以作爲一個字典又可以作爲一個優先隊列。二叉搜索樹的基本操作所花費的時間與這棵樹的高度成正比。對於一個n個結點的二叉搜索樹,他的期望時間爲O(lgn)


什麼是二叉搜索樹

二叉搜索樹顧名思義就是一顆可以用鏈表數據結構來表示的二叉樹來組織的。
在這裏插入圖片描述

  • 二叉搜索樹中的關鍵字總是滿足二叉搜索樹性質的方式來存儲的:

設x是二叉搜索樹中的一個結點。如果y是x左子樹的一個結點,那麼y.key<= x.key。如果y是x右子樹中的一個結點,那麼y.key >= x.key

根據其性質我們可以用一個簡單的遞歸算法來按序輸出二叉搜索樹的所有關鍵字,這種算法稱爲中序遍歷算法。因爲輸出的子樹根的關鍵字在左子樹的關鍵字和右子樹的關鍵字之間。此外還有先序遍歷後序遍歷

下面給出中序遍歷的僞代碼:

INORDER-TREE-WALK(x)
	if x!= NIL
		INORDER-TREE-WALK(x.left)
		print x.key
		INORDER-TREE-WALK(x.right)

定理12.1 如果x是一棵有n個結點子樹的根, 那麼調用INORDER-TREE-WALK(x)需要Θ(n)

查詢二叉搜索樹

我們經常需要查找存儲在二叉搜索樹內的關鍵字。這節將討論這些操作,並且說明在任何高度爲h的二叉搜索樹上,如何在O(h)時間內執行完每個操作。

查找

查找二叉搜索樹中一個給定關鍵字的結點。輸入一個指向樹根的指針一個關鍵字k,如果這個結點存在,TREE-SEARCH返回一個關鍵字爲k的結點的指針,否則返回NIL。

TREE-SEARCH(x, k)
if x == NIL or k == x.key
	return x
if k < x.key
	return TREE-SEARCH(x.left, k)
else
	return TREE-SEARCH(x.right, k)

我們可以看出,這個算法的運行時間爲O(h),其中h就是這棵樹的高度。
書中還說了一種迭代法,就是用while循環來展開遞歸,這種效率要高很多:

ITERACTIVE-TREE-SEARCH(x, k)
while x != NIL and k != x.key
	if k < x.key
		x = x.left
	else x = x.right
return x

最大關鍵字元素和最小關鍵字元素

通過從樹根開始沿着left孩子指針知道遇到一個NIL,我們總能在一顆二叉搜索樹中找到一個元素。因此下列僞代碼指出可以查找到最小元素的指針,這裏假設不是NIL:

TREE-MINIMUM(x)
while x.left != NIL
	x = x.left
return x

相似的,最大元素查找方法是一樣的:

TREE-MIXIMUM(x)
while x.right != NIL
	x = x.right
return x

前驅和後繼

給定一棵二叉搜索樹中的一個結點,有時候需要按中序遍歷的次序查找它的後繼。如果所有的關鍵字互不相同,則一個結點x的後繼是大於工x. key的最小關鍵字的結點。一棵二叉搜索樹的結構允許我們通過沒有任何關鍵字的比較來確定一個結點的後繼。如果後繼存在,下面的過程將返回一棵二叉搜索樹中的結點x的後繼;如果x是這棵樹中的最大關鍵字,則返回NIL.

TREE-SUCCESSOR(X)
if x.right != NIL
	return TREE-MINIMUN(x.right)
y = x.p
while y != NIL and x == y.right
	x = y
	y = y.p
return y

定理12.2.
在一棵高度爲h的二叉搜索樹上,動態集合上的操作SEARCH、MINIMUM、MAXIMUM、SUCCESSOR和PREDECESSOR可以在O(h)時間內完成。

插入和刪除

插入和刪除操作會引起二叉搜索樹表示的動態集合的變化。一定要修改數據結構來反映這個變化,但修改要保證二叉搜索樹性質的成立。正如下面將要看到的,插入一個新的結點帶來的樹要修改的相對簡單一點,而刪除的處理有些複雜。

插入

要將一個新值v插入到一顆二叉搜索樹T中,需要調用過程TREE-INSERT。該過程以結點z作爲輸入,其中z.key = v, z.left = NIL, z.right = NIL。這個過程要修改T和z的某些屬性,來吧z插入到樹中的相應位置上。

TREE-INSERT(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

下圖詮釋了插入操作的過程:
在這裏插入圖片描述
即向一個二叉搜索樹中插入一個x結點,只需要不斷比較x->key與當前結點z->key的大小,若小於,則肯定向z的左子樹插入,否則向z的右子樹插入,循環比較,直到遇到當前結點的左/右子樹爲空爲止。

刪除

刪除z結點操作要比較麻煩一些,因爲要保持二叉搜索樹的性質:

  • 如果z沒有孩子結點,那麼只是簡單的將他刪除,並修改他的父結點,用NIL作爲孩子來替換z。
  • 如果只有一孩子,那麼將這個孩子提升到樹中z的位置上,並修改z的父結點,用z的孩子來替換z
  • 如果有兩個孩子
    找出要刪除結點z的後繼結點y,這個結點y一定位於結點z的右子樹中且是z的右子樹中最小的一個元素,所以結點y一定沒有左孩子。
    分兩種情況:
    1.結點y是結點z的右孩子:這時用y替換z的位置,結點y之前的右子樹還是它的右子樹,結點z之前的左子樹作爲y的左子樹。
    在這裏插入圖片描述
    2.結點y不是結點x的右孩子:先用y的右子樹x替換y位置,接着用y替換z的位置。
    在這裏插入圖片描述

總結

因爲數學水平有限,對隨機構建二叉搜索樹沒有做記錄。還有刪除操作還不是特別清楚,不過先記錄下來,寫代碼實踐的時候再慢慢的消化吧。然後就是二叉搜索樹的時間,平均下來可以類似於折半查找,Θ(lgn)的時間,但是當搜索樹完全不平衡時就可能達到O(n)的時間,因此二叉搜索樹的時間複雜度爲O(lgn)~O(n)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章