1. 程式人生 > >BST-二叉查詢樹

BST-二叉查詢樹

二叉查詢樹相比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; }

刪除

刪除分三種情況:

  1. 刪除點沒有兒子(也就是葉子),直接刪之。
  2. 刪除點有一個兒子,若有左兒子則把 將刪除點的父結點跟左兒子連起來,右兒子同理。
  3. 刪除點有左右兒子,我們選一個比刪除點次大的點來頂替就行,次大可以是:左子樹裡的最大(左子樹裡的最右) 或者 右子樹裡的最小(右子樹裡的最左)。

如下圖,圖中的第三種情況是取右子樹裡的最小替換刪除點,下面程式碼是取左子樹裡的最大。圖片原地址戳這
topcoder圖

實現中我們需要讓刪除點的父親指向刪除點的兒子,但我們沒有父節點指標怎破?可以用遞迴實現, 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;
}

參考

推薦兩個資料結構演示 地址1 地址2