二叉排序樹 BST 的思路及C語言實現

2021-07-12 05:38:43 字數 4308 閱讀 6159

請注意,為了能夠更好的理解二叉排序樹,我建議各位在看**時能夠設定好斷點一步一步跟蹤函式的執行過程以及各個變數的變化情況

在進行動態查詢操作時,如果我們是在乙個無序的線性表中進行查詢,在插入時可以將其插入表尾,表長加1即可;刪除時,可以將待刪除元素與表尾元素做個交換,表長減1即可。反正是無序的,當然是怎麼高效怎麼操作。但如果是有序的呢?回想學習線性表順序儲存時介紹的順序表的缺點,就在於插入刪除操作的複雜與低效。

正因為如此,我們引入了鏈式儲存結構,似乎這樣就能解決上面遇到的問題了。但細想一下,我們要進行的是查詢操作,而鏈式結構的好處在於插入刪除,但在查詢方面則顯得無能為力。那麼有沒有一種結構能讓我們既提高查詢的效率,又不影響插入刪除的效率呢?

先從最簡單的情況考慮,我們現在有乙個只有乙個資料元素的集合,假設為,現在我需要查詢其中是否含有』56』這個數字,沒有則插入;那麼很顯然執行該操作後集合變成了,繼續查詢集合中是否有』16』這個元素,沒有則插入並且不改變原先的有序性。似乎要插入新資料就必須做移動。有沒有辦法不移動呢?有人想到了用二叉樹來儲存資料,讓20做根節點,56比20大所以做右子樹,16比20小所以做左子樹,這樣不就不用移動其位置了嗎?思路見下圖

二叉排序樹或者是一棵空樹,或者是一棵有下列性質的二叉樹:

1.若它的左子樹不為空,則它左子樹上所有結點的值必然小於根結點的值

2.若它的右子樹不為空,則它右子樹上所有結點的值必然大於根結點的值

3.它的左右子樹也為二叉排序樹

之所以要構造這樣一棵樹,不是為了排序,而是為了提高查詢,插入和刪除的效率

要構建一棵二叉排序樹並實現相關操作,首先應理解下列的三個操作

給定key值,首先將其與根節點比較,相等則返回根結點,否則將其與根結點值進行比較,小於根結點則在左子樹中遞迴查詢;大於根結點則在右子樹中遞迴查詢,**如下:

/*在bst中查詢值為key的元素,f為指向雙親的指標,p用來返回查詢路徑上最後乙個結點*/ 

status searchbst(bitree t,int key,bitree f,bitree *p)

if(t->data==key)

else

}

要進行插入,首先要在bst中進行查詢,若key值已經存在,則應返回error;不存在時,由於第一步的search操作已經返回了查詢路徑上的最後乙個結點,只需要把key值與最後乙個節點的值進行比較,比它小則為左子樹,反之為右子樹,**如下:

/*查詢key是否存在於bst中,如果不存在,則插入key到合適位置*/

status insertbst(bitree *t,int key)

insert->

data

=key;

insert->lchild=

null;

insert->rchild=

null;

// if(*t==found)

/*如果found空,說明根節點值應為key*/

if(!found)

*t=insert;

else

}/*如果找到了,返回error*/

else

return error;

}

由於二叉排序樹具有比二叉樹更多的特性,所以刪除操作就變得更複雜,不能因為刪除乙個結點導致整棵樹不滿足bst的性質。所以應該分三種情況進行討論:

1.刪除的是葉子節點

2.刪除的是只有左子樹或右子樹的節點

3.刪除的結點既有左子樹也有右子樹

對於第一種情況,直接刪除即可,葉子結點對整棵樹的性質並沒有影響

對於第二種情況,只有左或右子樹,只需要讓待刪除結點的指標指向其子樹即可,同時釋放原根結點指標即可

對於既有左子樹又有右子樹的情況,就比較複雜了,如圖所示:要刪除下圖中的值為47的節點,並且還要保持其是一棵bst,如何解決呢?

我們當然可以刪除47之後再把其子結點上的值插入回原先的bst中,但是這樣做效率十分低下而且會改變二叉樹的深度,我們換個角度考慮,在47的子結點中有沒有哪個值可以代替47的位置呢?尋找一番之後發現37和48好像沒問題,進一步思考,37和48能夠替代47的原因在於:當我們中序遍歷以47為根的二叉樹時,37和48是47的直接前驅和後繼。這樣我們就得到了乙個結論,刪除操作時,待刪除結點在中序遍歷的直接前驅或後繼可以替代其位置。配圖一張說明思路:

看了可能覺得思路清晰一些了,下面看**吧:

/*刪除bst中某一結點,並重接其左右子樹*/

status delete(bitree *t)

/*這裡要注意一定要寫成else if,否則會出現錯誤*/

else

if((*t)->rchild==null)

else

/*smove指向待刪除結點的直接前驅*/

(*t)->data=smove->data;/*首先把smove指向的值賦給待刪除結點,下一步要做的是刪除smove並且把smove的孩子結點接好*/

if(qmove!=(*t))/*如果qmove不指向待刪除結點,則把smove的左子樹賦給qmove的右子樹*/

qmove->rchild=smove->lchild;

else

qmove->lchild=smove->lchild;/*否則把smove的左子樹賦給qmove的左子樹*/

free(smove);

}return ok;

} /*查詢bst中是否存在key,若存在則刪除key*/

status deletebst(bitree *t,int key)

}/*此處順便附上主程式*/

intmain

(void)

; bitree

t=null,p;

for(i=0;i<10;i++)

deletebst

(&t,93);

deletebst

(&t,47);

inorder

(t);//中序遍歷

// searchbst

(t,35,null,&p);

// printf

("%d",p->data);

return 0;

}

前兩種情況比較簡單,不再贅述,下面分析第三種情況

第一步:宣告兩個指標,qmove指向待刪除結點,smove指向待刪除結點的左子樹之後讓smove往右子樹移動,同時qmove記錄smove的位置,這一步是為了找到待刪除結點的直接前驅

第二步:將smove的值賦給根節點,如果這時qmove沒有指向根結點,則讓qmove的右子樹指向smove的左子樹;否則qmove的左子樹指向smove的左子樹

第三步,釋放smove

下面這組展示了這段**執行時的每一步的狀態:

至此,刪除操作才算告一段落

二叉排序樹採用鏈式儲存,有較好的插入刪除的時間效能;查詢方面,最好情況是1次找到,最差情況也不會超過樹的深度。因此,查詢方面的效能取決於輸的形狀,比如有下面這樣的兩棵樹:

兩棵樹的資料全部相同,注意右邊的樹也是二叉排序樹,只不過其元素由於是從小到大排列的導致成了一棵右斜樹,要查詢99這個元素,左邊的樹只需要兩次比較,而右邊要 10次比較,差異非常大,已經失去了二叉排序樹的意義,因此,如何讓二叉樹兩邊變得均衡一些,是我們下乙個需要研究的問題。

二叉排序樹BST

二叉排序樹 binary sort tree 又稱二叉查詢樹 binary search tree 亦稱二叉搜尋樹。二叉排序樹的性質 左子樹上所有結點的值均小於或等於它的根結點的值 右子樹上所有結點的值均大於或等於它的根結點的值 左 右子樹也分別為二叉排序樹 如圖是乙個bst。有了這種性質,bst的...

二叉排序樹 BST

二叉排序樹,又叫二叉查詢樹,它或者是一棵空樹 或者是具有以下性質的二叉樹 1.若它的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值 2.若它的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值 3.它的左右子樹也分別為二叉排序樹。1.1 排序二叉樹之插入操作 已知乙個關鍵字值為key的結...

BST(二叉排序樹)

二叉查詢樹 binary search tree 又 二叉搜尋樹,二叉排序樹 它或者是一棵空樹,或者是具有下列性質的二叉樹 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值 它的左 右子樹也分別為二叉排序樹。查詢二叉搜尋樹 遞...