日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入学习二叉树(四) 二叉排序树

發(fā)布時(shí)間:2025/3/12 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入学习二叉树(四) 二叉排序树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

深入學(xué)習(xí)二叉樹(四) 二叉排序樹

1 前言

數(shù)據(jù)結(jié)構(gòu)中,線性表分為無序線性表和有序線性表。
無序線性表的數(shù)據(jù)是雜亂無序的,所以在插入和刪除時(shí),沒有什么必須遵守的規(guī)則,可以插入在數(shù)據(jù)尾部或者刪除在數(shù)據(jù)尾部。但是在查找的時(shí)候,需要遍歷整個(gè)數(shù)據(jù)表,導(dǎo)致無序線性表的查找效率低。
有序線性表的數(shù)據(jù)則相反,查找數(shù)據(jù)時(shí)的時(shí)候因?yàn)閿?shù)據(jù)是有序的,可以用二分法、插值法、斐波那契查找法來實(shí)現(xiàn)。但是,當(dāng)進(jìn)行插入和刪除操作時(shí),需要維護(hù)表中數(shù)據(jù)的有序性,會(huì)耗費(fèi)大量的時(shí)間。
那么,我們希望找到一種數(shù)據(jù)結(jié)構(gòu),既可以有較高的插入和刪除效率,并且具備較高的查找效率,因此,二叉排序樹應(yīng)運(yùn)而生。

2 二叉排序樹

2.1 定義

二叉排序樹(Binary Sort Tree),又稱二叉查找樹(Binary Search Tree),也稱二叉搜索樹。二叉排序樹或者是一棵空樹,或者是具有下列性質(zhì)的二叉樹:

(1)若左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于或等于它的根結(jié)點(diǎn)的值;
(2)若右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于或等于它的根結(jié)點(diǎn)的值;
(3)左、右子樹也分別為二叉排序樹;

2.2 構(gòu)造一棵二叉排序樹

現(xiàn)有序列:61 87 59 47 35 73 51 98 37 93

構(gòu)造過程如下:
1)索引 i = 0,A[i] = 61,結(jié)點(diǎn)61作為根結(jié)點(diǎn),如圖2.1:

2)索引 i = 1,A[1] = 87, 87 > 61,且結(jié)點(diǎn)61右孩子為空,故81為61結(jié)點(diǎn)的右孩子,如圖2.2:

3)索引 i = 2,A[i] = 59,59 <61,且結(jié)點(diǎn)61左孩子為空,故59為61結(jié)點(diǎn)的左孩子,如圖2.3:

4)索引 i = 3,A[3] = 47,47 < 59,且結(jié)點(diǎn)59左孩子為空,故47為59結(jié)點(diǎn)的左孩子,如圖2.4:

5)索引 i = 4,A[4] = 35,35 < 47,且結(jié)點(diǎn)47左孩子為空,故35為47結(jié)點(diǎn)的左孩子,如圖2.5:

采用同樣規(guī)則遍歷整個(gè)數(shù)組得到如圖2.6所示的一棵排序二叉樹。

2.3 二叉排序樹查找

由二叉樹的遞歸定義性質(zhì),二叉排序樹的查找同樣可以使用如下遞歸算法查找。

如果樹是空的,則查找結(jié)束,無匹配。
如果被查找的值和根結(jié)點(diǎn)的值相等,查找成功。否則就在子樹中繼續(xù)查找。如果被查找的值小于根結(jié)點(diǎn)的值就選擇左子樹,大于根結(jié)點(diǎn)的值就選擇右子樹。

在理想情況下,每次比較過后,樹會(huì)被砍掉一半,近乎折半查找。
遍歷打印可以使用中序遍歷,打印出來的結(jié)果是從小到大的有序數(shù)組。
查找代碼:

typedef int Status; /* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼,如OK等 */ /* 二叉樹的二叉鏈表結(jié)點(diǎn)結(jié)構(gòu)定義 */ typedef struct BiTNode /* 結(jié)點(diǎn)結(jié)構(gòu) */ {int data; /* 結(jié)點(diǎn)數(shù)據(jù) */struct BiTNode *lchild, *rchild; /* 左右孩子指針 */ } BiTNode, *BiTree;/* 遞歸查找二叉排序樹T中是否存在key, */ /* 指針f指向T的雙親,其初始調(diào)用值為NULL */ /* 若查找成功,則指針p指向該數(shù)據(jù)元素結(jié)點(diǎn),并返回TRUE */ /* 否則指針p指向查找路徑上訪問的最后一個(gè)結(jié)點(diǎn)并返回FALSE */ Status SearchBST(BiTree t, int key, BiTree f, BiTree *p) { if (!t) /* 查找不成功 */{ *p = f; return FALSE; }else if (key == t->data) /* 查找成功 */{ *p = t; return TRUE; } else if (key < t->data) return SearchBST(t->lchild, key, t, p); /* 在左子樹中繼續(xù)查找 */else return SearchBST(t->rchild, key, t, p); /* 在右子樹中繼續(xù)查找 */ }

對(duì)于圖2.6所示的二叉排序樹,若查找結(jié)點(diǎn)key為47則可以查找成功,若查找結(jié)點(diǎn)key為75,樹中不存在key為75的結(jié)點(diǎn),故查找失敗,則查找指針p指向查找路徑的最后一個(gè)結(jié)點(diǎn),即結(jié)點(diǎn)73。

2.4 二叉排序樹插入

二叉排序的插入是建立在二叉排序的查找之上的,插入一個(gè)結(jié)點(diǎn),就是通過查找發(fā)現(xiàn)該結(jié)點(diǎn)合適插入位置,把結(jié)點(diǎn)直接放進(jìn)去。 其實(shí)在2.2節(jié)中一步步構(gòu)造二叉排序樹的過程中就是結(jié)點(diǎn)插入過程。由此可以得出二叉排序樹插入規(guī)則如下:

若查找的key已經(jīng)有在樹中,則p指向該數(shù)據(jù)結(jié)點(diǎn)。
若查找的key沒有在樹中,則p指向查找路徑上最后一個(gè)結(jié)點(diǎn)。

例如:若在圖2.6展示的二叉排序樹中插入結(jié)點(diǎn)數(shù)據(jù)為60的結(jié)點(diǎn)。
首先查找結(jié)點(diǎn)數(shù)據(jù)為60的結(jié)點(diǎn),二叉排序樹中不存在結(jié)點(diǎn)為60的結(jié)點(diǎn),因此查找失敗。此時(shí)查找指針p指向查找路徑最后一個(gè)結(jié)點(diǎn)即指向59結(jié)點(diǎn)。由于60>59且59結(jié)點(diǎn)右子樹為空,故將60結(jié)點(diǎn)作為59結(jié)點(diǎn)的右孩子,插入完成。插入后的二叉排序樹如圖2.8所示。


插入代碼:

struct BiTree {int data;BiTree *lchild;BiTree *rchild; };//在二叉排序樹中插入查找關(guān)鍵字key BiTree* InsertBST(BiTree *t,int key) {if (t == NULL){t = new BiTree();t->lchild = t->rchild = NULL;t->data = key;return t;}if (key < t->data) t->lchild = InsertBST(t->lchild, key);elset->rchild = InsertBST(t->rchild, key);return t; }//n個(gè)數(shù)據(jù)在數(shù)組d中,tree為二叉排序樹根 BiTree* CreateBiTree(BiTree *tree, int d[], int n) {for (int i = 0; i < n; i++)tree = InsertBST(tree, d[i]); }

2.5 二叉排序樹刪除

二叉樹的刪除可不再像二叉樹的插入那么容易了,以為刪除某個(gè)結(jié)點(diǎn)以后,會(huì)影響到樹的其它部分的結(jié)構(gòu)。
刪除的時(shí)候需要考慮以下幾種情況:

1)刪除結(jié)點(diǎn)為葉子結(jié)點(diǎn);
2)刪除的結(jié)點(diǎn)只有左子樹;
3)刪除的結(jié)點(diǎn)只有右子樹
4)刪除的結(jié)點(diǎn)既有左子樹又有右子樹。

考慮前三種情況,處理方式比較簡(jiǎn)單。
例如:若要?jiǎng)h除圖2.8中的結(jié)點(diǎn)93,則直接刪除該結(jié)點(diǎn)即可。刪除后二叉排序樹如圖2.9所示:

若要?jiǎng)h除的結(jié)點(diǎn)為結(jié)點(diǎn)35,結(jié)點(diǎn)35只有右子樹,只需刪除結(jié)點(diǎn)35,將右子樹37結(jié)點(diǎn)替代結(jié)點(diǎn)35即可。刪除后的二叉排序樹如圖2.10所示:

刪除只有左子樹的結(jié)點(diǎn)與此情況類似。

情況4相對(duì)比較復(fù)雜,對(duì)于待刪除結(jié)點(diǎn)既有左子樹又有右子樹的情形,最佳辦法是在剩余的序列中找到最為接近的結(jié)點(diǎn)來代替刪除結(jié)點(diǎn)。這種代替并不會(huì)影響到樹的整體結(jié)構(gòu)。那么最為接近的結(jié)點(diǎn)如何獲取呢?
可以采用中序遍歷的方式來得到刪除結(jié)點(diǎn)的前驅(qū)和后繼結(jié)點(diǎn)。選取前驅(qū)結(jié)點(diǎn)或者后繼結(jié)點(diǎn)代替刪除結(jié)點(diǎn)即可。
例如:待刪除的結(jié)點(diǎn)為47,圖2.8中二叉排序樹的中序遍歷序列為35 37 47 51 59 60 61 73 87 93 98。則結(jié)點(diǎn)47的前驅(qū)結(jié)點(diǎn)為37,則直接將37結(jié)點(diǎn)替代47結(jié)點(diǎn)即可。替換后的二叉排序樹如圖2.11所示:

刪除代碼:

/* 若二叉排序樹T中存在關(guān)鍵字等于key的數(shù)據(jù)元素時(shí),則刪除該數(shù)據(jù)元素結(jié)點(diǎn), */ /* 并返回TRUE;否則返回FALSE。 */ Status DeleteBST(BiTree *T,int key) { if(!*T) /* 不存在關(guān)鍵字等于key的數(shù)據(jù)元素 */ return FALSE;else{if (key==(*T)->data) /* 找到關(guān)鍵字等于key的數(shù)據(jù)元素 */ return Delete(T);else if (key<(*T)->data)return DeleteBST(&(*T)->lchild,key);elsereturn DeleteBST(&(*T)->rchild,key);} } /* 從二叉排序樹中刪除結(jié)點(diǎn)p,并重接它的左或右子樹。 */ Status Delete(BiTree *p) {BiTree q,s;if((*p)->rchild==NULL) /* 右子樹空則只需重接它的左子樹(待刪結(jié)點(diǎn)是葉子也走此分支) */{q=*p; *p=(*p)->lchild; free(q);}else if((*p)->lchild==NULL) /* 只需重接它的右子樹 */{q=*p; *p=(*p)->rchild; free(q);}else /* 左右子樹均不空 */{q=*p; s=(*p)->lchild;while(s->rchild) /* 轉(zhuǎn)左,然后向右到盡頭(找待刪結(jié)點(diǎn)的前驅(qū)) */{q=s;s=s->rchild;}(*p)->data=s->data; /* s指向被刪結(jié)點(diǎn)的直接前驅(qū)(將被刪結(jié)點(diǎn)前驅(qū)的值取代被刪結(jié)點(diǎn)的值) */if(q!=*p)q->rchild=s->lchild; /* 重接q的右子樹 */ elseq->lchild=s->lchild; /* 重接q的左子樹 */free(s);}return TRUE; }

3 結(jié)語

二叉排序樹是一種查找與插入效率均較為高效的數(shù)據(jù)結(jié)構(gòu),同時(shí),二叉排序樹也是二叉樹學(xué)習(xí)中的重點(diǎn)與難點(diǎn)。希望通過本篇的學(xué)習(xí)能夠掌握二叉排序樹的查找、插入與刪除等基本操作,也希望讀者給出指導(dǎo)意見。

總結(jié)

以上是生活随笔為你收集整理的深入学习二叉树(四) 二叉排序树的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。