BST-二叉查詢樹
阿新 • • 發佈:2019-02-20
二叉查詢樹相比AVL和紅黑樹簡單多了,在刪除時無需旋轉,但樹高不平衡,有可能出現樹高 = 結點樹的情況,本文介紹其C/C++程式碼實現。
定義
直接上維基百科,要麼是空樹要麼是符合以下性質的二叉樹
任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
任意節點的左、右子樹也分別為二叉查詢樹;
沒有鍵值相等的節點。
樣例如圖
特點是它的插入刪除查詢的平均複雜度都是O(log n),當節點有序的時候退化成連結串列O(n)複雜度(樹的全部結點都只有左/右子樹)
實現
結點
struct node
{
int val;
node *left, *right;
};
插入
插入很簡單,根據樹的大小關係去左/右定位即可,插入的點一定是新結點/沒有子樹的。
注意需要返回插入後新樹的根節點,不能返回插入點的指標。
// 插入:值不可重複,返回插入後的根節點
node* insert(node *root, int val) {
if (!root) {
root = new node();
root->val = val;
} else if (root->val < val) {
root->right = insert(root->right, val);
} else if (root->val > val) {
root->left = insert(root->left, val);
}
return root;
}
查詢
非遞迴寫法會明顯快於遞迴寫法,不需要壓/彈系統棧,若不存在返回NULL即可。
// 查詢:返回具有該值的結點指標
node* find(node *root, int val) {
while (root) {
if (root->val < val) {
root = root->right;
} else if (root->val > val) {
root = root->left;
} else {
return root;
}
}
return NULL;
}
刪除
刪除分三種情況:
- 刪除點沒有兒子(也就是葉子),直接刪之。
- 刪除點有一個兒子,若有左兒子則把 將刪除點的父結點跟左兒子連起來,右兒子同理。
- 刪除點有左右兒子,我們選一個比刪除點次大的點來頂替就行,次大可以是:左子樹裡的最大(左子樹裡的最右) 或者 右子樹裡的最小(右子樹裡的最左)。
如下圖,圖中的第三種情況是取右子樹裡的最小替換刪除點,下面程式碼是取左子樹裡的最大。圖片原地址戳這
實現中我們需要讓刪除點的父親指向刪除點的兒子,但我們沒有父節點指標怎破?可以用遞迴實現, root->right = del(root->right, val);
del() 返回刪除點的兒子,然後通過上一層去修改指標。
// 刪除 返回刪除後的根節點
// 情況1:同時有左右兒子,找到左兒子裡的最大值,替換上來
// 情況2:有0或1個兒子,有左兒子就讓左兒子替換當前節點,有右則右替換,都沒有就直接刪除
node* del(node *root, int val) {
if (!root) return NULL;
if (root->val < val) {
root->right = del(root->right, val);
} else if (root->val > val) {
root->left = del(root->left, val);
} else {
node *son;
if (root->left && root->right) { //左右都有,返回左子樹裡的最右,指標修改由上一層遞迴處理
son = root->left;
while (son->right) son = son->right;
son->right = root->right; //避免丟了刪除點的右子樹
}
else { //只有一個子樹,返回存在的那個子樹即可
if (root->left) son = root->left;
else if (root->right) son = root->right;
else son = NULL; //葉子結點沒有子樹,返回NULL給一層
}
delete root; //刪除點
return son;
}
return root;
}
更新
由於BST沒有像其他樹那樣的調整操作,所以只能先刪後增。
// 更新值:返回新樹根節點
node* update(node *root, int oldVal,int newVal) {
root = del(root,oldVal);
root = insert(root,newVal);
return root;
}
遍歷
// 中序遍歷:得出遞增結果
void inOrder(node *root) {
if (!root) return;
inOrder(root->left);
printf("%d ", root->val);
inOrder(root->right);
}
測試
int main() {
node *rt = NULL;
rt = insert(rt, 3);
rt = insert(rt, 5);
rt = insert(rt, 6); rt = insert(rt, 6);
rt = insert(rt, 8);
rt = insert(rt, 4);
rt = insert(rt, 10);
inOrder(rt); printf("\n");
rt = del(rt, 6);
inOrder(rt); printf("\n");
rt = del(rt,3);
inOrder(rt); printf("\n");
rt = update(rt,5,88);
inOrder(rt); printf("\n");
return 0;
}
參考