什么是二叉搜索樹
二叉搜索樹(英語:Binary Search Tree),(又:二叉搜索樹,二叉排序樹)它或者是一棵空樹,或者是具有下列性質的二叉樹:
- 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
- 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
- 它的左、右子樹也分別為二叉搜索樹。
二叉搜索樹如何儲存數值
如圖所示:
所有的節點,都滿足左子樹上的所有節點都比自己的小,而右子樹上的所有節點都比自己大這個條件。
二叉搜索樹的操作
因為二叉搜索樹的性質,二叉搜索樹能夠高效地進行如下操作:
- 插入一個數值;
- 查詢是否包含某個數值;
- 刪除某個數值
如果共有n個元素,那么平均每次操作需要O(logn)的時間。
接下來用C++來實現以上操作。首先定義節點結構體如下:
node* insert(node* p,int x)
{
if(!p)
{
auto q = new node(x);
return q;
}
else
{
if(x < p->val) p->lch = insert(p->lch,x);
else p->rch = insert(p->rch,x);
return p;
}
}
插入一個數值
如圖所示:
node* insert(node* p,int x)
{
if(!p) //空樹
{
auto q = new node(x);
return q;
}
else
{
if(x < p->val) p->lch = insert(p->lch,x);
else p->rch = insert(p->rch,x);
return p;
}
}
查詢是否包含某個數值
如圖所示:
bool find(node* p,int x)
{
if(!p) return false;
if(x == p->val) return true;
if(x < p->val) return find(p->lch,x);
else return find(p->rch,x);
}
刪除某個數值
數值的刪除比起之前提到的操作要稍微麻煩一些。例如,我們要刪除數值15。如果刪除了15所在的節點,那么它的兩個兒子10和17就懸空了。於是,把11提到15所在的位置就可以解決問題。如圖所示:
一般來說,需要根據下面幾種情況分別進行處理:
- 需要刪除的節點沒有左兒子,那么就把右兒子提上去。
- 需要刪除的節點的左兒子沒有右兒子,那么就把左兒子提上去。
- 以上兩種情況都不滿足的話,就把左兒子的子孫中最大的節點提到需要刪除的節點上。
node* remove(node* p,int x)
{
if(!p) return NULL;
if(x < p->val) p->lch = remove(p->lch,x);
else if(x > p->val) p->rch = remove(p->rch,x);
else
{
if(p->lch == NULL) //需要刪除的節點沒有左兒子
{
auto q = p->rch;
delete p;
return q;
}
else if(p->lch->rch == NULL) //需要刪除的節點的左兒子沒有右兒子
{
auto q = p->lch;
q->rch = p->rch;
delete p;
return q;
}
else
{
auto q = p->lch;
while(q->rch->rch != NULL) q = q->rch;
auto r = q->rch;
q->rch = r->lch;
r->lch = p->lch;
r->rch = p->rch;
delete p;
return r;
}
return p;
}
}
測試代碼
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int val;
node *lch,*rch;
node(int value): val(value),lch(NULL),rch(NULL){ }
};
node* insert(node* p,int x)
{
if(!p)
{
auto q = new node(x);
return q;
}
else
{
if(x < p->val) p->lch = insert(p->lch,x);
else p->rch = insert(p->rch,x);
return p;
}
}
bool find(node* p,int x)
{
if(!p) return false;
if(x == p->val) return true;
if(x < p->val) return find(p->lch,x);
else return find(p->rch,x);
}
node* remove(node* p,int x)
{
if(!p) return NULL;
if(x < p->val) p->lch = remove(p->lch,x);
else if(x > p->val) p->rch = remove(p->rch,x);
else
{
if(p->lch == NULL) //需要刪除的節點沒有左兒子
{
auto q = p->rch;
delete p;
return q;
}
else if(p->lch->rch == NULL) //需要刪除的節點的左兒子沒有右兒子
{
auto q = p->lch;
q->rch = p->rch;
delete p;
return q;
}
else
{
auto q = p->lch;
while(q->rch->rch != NULL) q = q->rch;
auto r = q->rch;
q->rch = r->lch;
r->lch = p->lch;
r->rch = p->rch;
delete p;
return r;
}
return p;
}
}
void printTree(node* root)
{
queue<node*> q;
q.push(root);
while(!q.empty())
{
auto p = q.front();q.pop();
if(p)
{
cout << p->val << " ";
q.push(p->lch);
q.push(p->rch);
}
}
cout << endl;
}
int main() {
node* root = insert(NULL,7);
insert(root,2);
insert(root,15);
insert(root,1);
insert(root,5);
insert(root,10);
insert(root,17);
insert(root,4);
insert(root,6);
insert(root,8);
insert(root,11);
insert(root,16);
insert(root,19);
if(find(root,15)) cout << "find 15" << endl;
else cout << "can not find 15" << endl;
if(find(root,3)) cout << "find 3" << endl;
else cout << "can not find 3" << endl;
printTree(root);
remove(root,15);
printTree(root);
return 0;
};
結果:
參考資料
- 《挑戰程序設計競賽》人民郵電出版社
- 二叉搜索樹_百度百科