《演算法導論》筆記 二叉搜尋樹

2021-06-06 21:13:04 字數 4698 閱讀 3147

演算法

資料結構

二叉搜尋樹(binary search tree)是經過一定地組織形成的有特定結構特徵的二叉樹,支援各種動態集合(dynamic set)操作(如insert、delete、maximum、minimum等等)。這些操作的時間複雜度與樹的高度成正比。一棵有n個節點的完全二叉樹(complete binary tree)的高度不超過lgn。因此,對於完全二叉樹的這些操作的最壞時間複雜度(worst-case time)為o(lgn)。但極端病態的二叉樹,形狀就與鍊錶是一樣的,那麼,各項操作的最壞時間複雜度將變為o(n)。

由隨機輸入資料構建的二叉搜尋樹的期望高度是o(lgn),也有一些經過更加複雜的構造規則而得到的二叉查詢樹(如紅黑樹、b樹等),對於一般的輸入都可以保持o(lgn)的期望高度。

二叉搜尋樹的結構特徵是:對於乙個節點x,它的鍵值為key[x],那麼x的左子樹中的任何乙個節點的鍵值,都不會大於key[x],x的右子樹中的任何乙個節點的鍵值都不會小於key[x]。

用c**進行實現乙個二叉搜尋樹,樹的節點只儲存int型的鍵值,並實現動態集合的基本操作,作為練習應該是ok了,呵呵。

先定義一些基本的結構,像樹的節點、對節點的基本操作,用於取節點的左右子節點和父節點、以及乙個樹「物件」:

cpp** 

// 樹節點 

typedef

struct

tagbstreenode bstreenode, *pbstreenode;    

#define getleft(pnode)   (pnode->left)    // 左子節點   

#define getright(pnode)  (pnode->right)   // 右子節點 

#define getparent(pnode) (pnode->parent)  // 父節點 

// 二叉查詢樹「物件」 

typedef

struct

tagbstree bstree, *pbstree;  

二叉搜尋樹的節點插入操作

將乙個節點插入二叉搜尋樹,且要保持二叉搜尋樹的結構特徵,關鍵是要找到合適插入的位置。

所以,插入操作時,先進行位置地查詢,方法是從根開始,比較要插入的節點的鍵值與樹中的每乙個節點的鍵值,如果大於該節點,則下乙個比較的位置移到該節點的右子節點。否則,下乙個比較的位置移到該節點的左子節點上。這樣一直比較到樹的最「底下」的節點x(葉節點)。然後視鍵值,把需要插入的節點設定為 x的左子點,或是右子節點。**如下:

cpp** 

void

tree_insert(pbstree ptree, pbstreenode pnode)  

pnode->parent = pparent;  

if(null == pparent)   

else

else

}  }  

最大值與最小值。

對於二叉搜尋樹,很容易找到最大值和最小值。只需要從根節點開始,不斷地向左子節點移到,到葉子,就是最小值。從根節點開始,不斷地向右子節點移到,到葉子,就是最大值。

cpp** 

pbstreenode tree_max(pbstreenode proot)  

return

proot;  

}  pbstreenode tree_min(pbstreenode proot)  

return

proot;  

}  

直接前趨

節點x的直接前趨節點y,就在樹中,鍵值比x小,但又最接近x的節點。

如果找到直接前趨節點呢?

1.首先,如果節點x有左子節點,那麼,x的直接前趨節點y一定是以x的左子節點為根的子樹中的最大鍵值節點。

證明,如下圖,圖中從x節點向上的路徑可能出現的兩種情況,我們稱1是向左,2是向右。

設x是黑色節點。從x向樹的上方攀登,只要是沿著右方向到達的節點(如果圖中2),都是鍵值比x大的節點(根據二叉查詢樹的性質,左子樹中的任何節點都小於根嘛),所以這些節點不可能是x的前趨(因為前趨節點的鍵值是小於x的)。如果沿著左方向到達的節點(如圖中1),那這個節點的鍵值確實比x的鍵值小,但是,它也絕不可能是x的直接前趨,因為x以及x的子節點,都是這個節點(z)的右子樹中的節點,所以z的鍵值一定小於x的左子樹中的任何乙個節點的鍵值,那麼它就失去作為x直接前趨的資格了(因為直接前趨是應該是鍵值最接近x的嘛)。對稱地,x是父節點的右節點時,情況也是一樣的。所以可以得出結論,如果節點x有左子節點,那麼x的直接前趨節點y一定是以x的左子節點為根的子樹中的最大鍵值節點。

2.如果x沒有左子樹呢?那麼,從上面的分析可知,x的直接前趨就一定是從x向上的路徑中,第一次「左」轉時的節點z。如果向上直到根節點的路徑都是右方向的,那麼這個x節點就沒有直接前趨節點了(也就是x是這二叉查詢樹最小的節點)。

直接後繼

直接後繼就只給出結論了:如果節點x有右子節點,那麼,x的直接前趨節點y一定是以x的左子節點為根的子樹中的最小鍵值節點。如果沒有右子節點,那麼x的直接後繼節點就是從x向上的路徑中第一次右轉時的節點。

證明與直接前趨是對稱的。

cpp** 

pbstreenode tree_successor(pbstreenode proot)  

pbstreenode pparent = getparent(proot);  

while

((null != pparent) && (proot == getright(pparent)))    

return

pparent;  

}  pbstreenode tree_predecessor(pbstreenode proot)  

pbstreenode pparent = getparent(proot);  

while

((null != pparent) && (proot == getleft(pparent)))    

return

pparent;  

}     

查詢

其實二叉查詢樹的查詢,應該很容易理解了,就是讓x指向根,比較x的鍵值是否與要找的鍵值相等,是,那麼x就要找的節點,如果不是,那麼x就根據值的大小向左或向右移動,以此不斷地向下,直到樹的末尾。

cpp** 

pbstreenode tree_search(pbstreenode proot, 

intval_key)  

return

proot;  

}  

刪除:

刪除乙個節點,最重要的也是想辦法在刪除動作後,仍然保持二叉查詢樹的特徵。

當然,我們會想用最容易的方法來保持它(複雜的話,就成有名有姓的樹了,像紅黑樹,在保證這二叉查基本特徵時,還維持了一些其它的特性,那就複雜鳥)。分三種情況:

1.如果被刪除的節點沒有子樹,好說,直接刪除就可以了,對二叉搜尋樹的基本特徵沒有任何影響。

2.如果被刪除的節點,只有乙個子樹呢?也好說,把那唯一的子樹掛到需要刪除節點的父節點上相應的子樹位置,然後刪除掉需要刪除的節點。也不會對二叉樹的基本特徵產生不良影響。

3.如果被刪除節點有兩個子樹呢?那就不好辦了,關鍵是要保住樹的基本性質不動搖,呵呵。怎麼辦呢?

我想,先化為已經解決的問題,那不就好辦了嗎?hoho,1和2是已經解決的,那麼化為1或2來解決嘛。設要刪除的子節點是x,那麼我們隨意選取乙個子樹,從底部開始,把這子樹上所有的節點,乙個乙個從樹上「摘下來」,存著。然後x就成為乙個只有一顆子樹的節點,然後就適用第2種情況啦。刪除完x後,再把存著的那些節點重新插入到樹上,不就得了。呵呵,笨辦法,但我確實這麼想過。

再想想其它辦法。要想不影響樹的特性,那麼最簡單的想法是如果能只動x以及x以下的節點,那麼樹的其它部分肯定是ok的。那只要處理x以及x的子樹就行了。想想,如果能在子樹中找乙個節點來替代x的位置,並保證新的子樹也是滿足二叉查詢樹要求的,這樣改動量可能就比較小了。那麼找哪個節點來代替它呢?當然是鍵值最接近x的,這樣二叉樹的特徵就比較容易保持嘛。鍵值最接近x的,上面已經說過了,就是直接前趨和直接後繼。正好,對於有兩個子樹的x來說,它的直接前趨和直接後繼都是在它的子樹中的,分別是左子樹的最大值、右子樹的最小值。而且,從子樹中取下這兩個節點(取下來幹嘛?代替需要刪除的x節點唄),也是比較容易的,因為「最大」「最小」值節點,最多擁有不超過乙個子節點(不然它絕對不夠格做最大或最小)。而沒有子節點和只有乙個子節點的節點刪除,是我們已經會啦~~~。好,那麼就取前趨或後繼就來代替需要刪除的節點,問題就解決了。

cpp** 

pbstreenode tree_delete(pbstree ptree, pbstreenode pdel)  

else

pbstreenode pchildofdel;  

pchildofdel = (getleft(preldel)!= null ? getleft(preldel) : getright(preldel));  

if(pchildofdel != null)   

if(null == getparent(preldel))   

else

else

}  if(preldel != pdel)   

return

preldel;  

}  

演算法導論 二叉搜尋樹

搜尋樹資料結構支援許多動態集合操作,包括search minimum maximum predecessor successor insert和delete等。因此,我們使用一棵搜尋樹既可以作為乙個字典又可以作為乙個優先佇列。二叉搜尋樹上的基本操作所花費的時間與這棵樹的高度成正比。對於乙個有n個結點...

演算法導論二叉搜尋樹

兩天寫完的,沒有認認真真的測試,可能有些bug,自己測試的資料能過,但是我的初始化有些問題,不知道該怎麼表示 二叉搜尋樹 include include using namespace std struct node void inorder search node root1 node tree ...

演算法導論筆記 12二叉搜尋樹

1 概念 二叉搜尋樹也叫二叉排序樹,它支援的操作有 search,minimum,maximum,predecessor,successor,insert,delete。所以,一顆二叉搜尋樹既可以作為乙個字典,又可以作為乙個優先佇列。二叉搜尋樹的基本操作時間與這棵樹的高度成正比。二叉搜尋樹的高度可以...