基本資料結構 —— 二叉搜尋樹(C++實現)
什麼是二叉搜尋樹
二叉搜尋樹(英語: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; };
結果:

參考資料
- 《挑戰程式設計競賽》人民郵電出版社
- 二叉搜尋樹_百度百科