二叉排序樹(BST)問題
昨天晚上和RE聊完天之後,單JJ問了我一個二叉排序樹問題,那個題目不難,就是給一個了一棵二叉排序樹作為模型,有給了若干棵二叉排序樹,問這幾棵樹和模型樹是否是同一棵樹。這麼一個小問題搞了半個小時,簡直了。這個題目只需要寫一個BST建樹函式和求前序和中序的函式(一棵唯一的樹有唯一的前中序或中後序),然後對模型樹和這若干樹進行前中序比較,如果完全相同,就可以判斷為同一棵樹。 昨天才發現沒有寫過二叉排序樹的建樹,也發現《資料結構》木有講二叉排序樹,今天算是總結一些BST問題。
什麼BST
二叉排序樹(Binary Sort Tree)又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹。 它或者是一棵空樹;或者是具有下列性質的二叉樹: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別為二叉排序樹; 又是一個遞迴的定義。
說白了,就是:左孩子的二叉樹。 也就是因為BST的特性,導致他在元素查詢的時候比普通二叉樹更有優勢(有點像二分),但整棵樹要滿足這個性質,也註定讓BST在建立和刪除的時候和普通二叉樹不一樣。
BST的插入
BST的插入是建立BST的基礎,在插入一棵BST的時候一定要注意滿足左
void inst(node *rt,node *root) { if(rt->data data) { if(root->lc == NULL) root->lc = rt; inst(rt,root->lc); }else { if(root->rc == NULL) root->rc = rt; inst(rt,root->rc); } }
建立BST
建立BST是在插入BST的基礎上的,因為建立一棵BST的過程就是一個一個將結點插入到樹的過程。
node *build(char *s) { node *root = NULL; while(*s !='�') { node *rt = new node; rt->data = *(s++); rt->lc = NULL; rt->rc = NULL; if(root == NULL) root = rt; else inst(rt,root); } return root; }
BST的查詢
BST也被稱為二叉查詢樹的原因就是它的查詢優勢,二分,在每經過一個結點的時候,可以放棄樹的另一枝而不用遍歷整棵樹。
node *findnode(int data,node *root)
{
if(root == NULL)
return NULL;
if(data == root->data)
return root;
else if(data data)
return findnode(data,root->lc);
else
return findnode(data,root->rc);
}
BST的刪除
BST的整棵樹的刪除還是很簡單的,一個遞迴,和刪除整個二叉樹一樣,難的是刪除BST的一個結點。
刪除整棵樹
void deltree(node *root)
{
if(root == NULL)
return ;
deltree(root->lc);
deltree(root->rc);
delete(root);
}
刪除結點
刪除結點是建立在查詢的基礎上的,
node *finddlenode(int data,node *root)
{
if(root)
{
if(data == root->data)
root = delnode(root);
else if(data data)
root->lc = finddelnode(data,root->lc);
else
root->rc = finddelnode(data,root->rc);
}
return root;
}
刪除結點,對於BST來講,就會破壞整棵樹的規律,那麼當我們刪除一個結點之後,可能會遇到4種情況
- 刪除的是葉子,還是一棵BST
- 刪除的結點只有左孩子
- 刪除的結點只有右孩子
- 刪除的結點有兩個孩子
對於第一種情況,很好辦,可以不用考慮,第二種情況以及以後,如果簡單考慮,可以認為變成了2-3個BST森林,我們只需要再把它們合成一棵樹便好。 對於再把這幾棵樹合成一棵樹,有兩種方法(都是利用右枝比根結點大的特殊性) 方法一:
- 若p有左子樹,找到其左子樹的最右邊的葉子結點r,用該葉子結點r來替代p,把r的左孩子作為r的父親的右孩子(相當於把r的右枝移上去而把左枝留在原有位置)。
- 若p沒有左子樹,直接用p的右孩子取代它。
方法二:
- 若p有左子樹,用p的左孩子取代它;找到其左子樹的最右邊的葉子結點r,把p的右子樹作為r的右子樹。
- 若p沒有左子樹,直接用p的右孩子取代它。
兩種方法都不錯,第一種感覺更簡單些。 第一種程式碼
node * delNode(node *root)
{
node *rnt;
if (root->lc)
{
node *rt = root->lc; //rt指向其左子樹;
while(rt->rc != NULL)//搜尋左子樹的最右邊的葉子結點rt
{
rt = rt->rc;
}
rt->rc = root->rc;
rnt = root->lc; //rnt指向其左子樹;
}
else
{
rnt = root->rc; //rnt指向其右子樹;
}
delete(root);
return rnt;
}
歡迎到微信裡去當吃瓜群眾