【算法導論】二叉搜索樹的實現

二叉搜索樹的實現

二叉搜索樹的特點是,A.left<=A<=A.right 。對於一個節點來說,左子樹是比該節點小的,右子樹是比該節點大的。

1.首先給出二叉樹的結構體

由四個部分組成,節點值,左子樹,右子樹,父節點。

struct node{
    int val;
    node* left;
    node* right;
    node* p;
    node(int x) : val(x), left(NULL), right(NULL) ,p(NULL){}
};

2.查詢操作

在一個樹中查詢操作的時間是O(h),h 爲樹的高度。如果待查詢的值在比當前節點的值小,則該值在節點的左子樹,反之右子樹。

node* Tree_search(node* x, int k){
    if (x == NULL || x->val == k){
        return x;
    }
    if (k < x->val){
        x = x->left;
        return Tree_search(x, k);
    }
    else
        return Tree_search(x->right, k);
}

node* It_tree_search(node* x,int k){
    while (x != NULL&&k != x->val){
        if (k < x->val) x = x->left;
        else x = x->right;
    }
    return x;
}

遞歸方法和迭代方法查詢。

3. 找出子樹的最大值和最小值

子樹的最大值一定在子樹的右子樹上,反之左子樹。

node* Tree_minmum(node* x){
    while (x->left != NULL){
        x = x->left;
    }
    return x;
}
node* Tree_maxmum(node*x){
    while (x->right != NULL){
        x = x->right;
    }
    return x;
}

4.節點的前驅和後繼

前驅就是小於該節點值的最大值。後繼就是大於該節點值的最小值。以後繼爲例,如果節點的右子樹不爲空,那麼後繼節點是右子樹上的最小值。因爲右子樹上都是大於該節點的值,要找後繼,就是找右子樹上的最小值。如果右子樹爲空,就是向上找,找到一個節點,這個節點滿足,要查詢的節點在這個節點的左子樹上。那麼該節點就是後繼節點。解釋:由於右子樹爲空,說明節點的子樹中沒有比節點x大的值,因此需要向父節點上找,如果節點x是在父節點的右子樹上的,那麼父節點y的子樹中也沒有比x大的,需要繼續找父節點的父節點,直到節點在父節點的左子樹上時,那麼父節點就是後繼節點,因爲父節點的右子樹和父節點都比該節點大,因此該節點就是大於x的最小值。

node* Tree_successor(node* x){
    if (x->right != NULL){
        return Tree_minmum(x->right);
    }
    node* y = x->p;
    while (y != NULL&&x == y->right){
        x = y;
        y = y->p;
    }
    return y;
}
node* Tree_predecessor(node*x){
    if (x->left != NULL){
        return Tree_maxmum(x->left);
    }
    node* y = x->p;
    while (y != NULL&&x == y->left){
        x = y;
        y = y->p;
    }
    return y;
}

5.插入

插入操作比較簡單,就是先找到插入值應該在的位置,把該值作爲葉子節點插入就可以了。

void Tree_insert(node* &T,node* z){
    node* y = NULL;
    node* x = T;
    while (x != NULL){
        y =x;
        if (z->val > x->val){
            x = x->right;
        }
        else{
            x = x->left;
        }
    }
    z->p = y;
    if (y == NULL) T = z;
    else if (y->val < z->val){
        y->right = z;
    }
    else{
        y->left = z;
    }
}

6.刪除

刪除一個節點可能會帶來二叉搜索樹的性質不滿足。這裏爲了方便進行樹的移動和交換定義了一個子過程。

void Transplant(node* &T, node*u, node* v){
    if (u->p == NULL){
        T = v;
    }
    else if (u = u->p->left){
        u->p->left = v;
    }
    else{
        u->p->right = v;
    }
    if (v != NULL){
        v->p = u->p;
    }
}

刪除可以分爲三種情況,1.要刪除的z只有一個子樹,那麼可以直接將z刪掉,然後將子樹變爲z的父節點的子樹。2.z有兩個子樹,那麼需要先找到z的後繼y(爲什麼要找後繼呢?把後繼的值y放在z的位置,因爲z的左子樹本來就比z小,後繼比z大,且左子樹內沒有發生變化。所以對於左子樹滿足性質。對於右子樹,y是右子樹中最小的值,所以也滿足性質。)後繼y是沒有左子樹的。如果y的父節點不是z,那麼將y的右子樹變爲y的父節點的左子樹,然後用y來替換z。如果z是y的父節點,那麼簡單的將y把z替換就可以。

    void Tree_delete(node* &T, node *z){
    if (z == NULL){
        return;
    }
    if (z->left == NULL){
        Transplant(T, z, z->right);
    }
    else if (z->right == NULL){
        Transplant(T, z, z->right);
    }
    else{
        node* y = Tree_minmum(z->right);
        if (y->p != z){
            Transplant(T, y, y->right);
            y->right = z->right;
            y->right->p = y;
        }
        Transplant(T, z, y); 
        y->left = z->left;
        y->left->p = y;

    }
    return;
}

7構建樹

就是簡單的將值插入到一個空樹裏面。

node* build_tree(int*a,int len){
    node* root =NULL;
    for (int i = 0; i < len; ++i){
        node* z= new node(0);
        z->val = a[i];
        Tree_insert(root, z);
    }
    return root;
}

8.測試過程

int main(){
    int r4[10] = { 12, 16, 3, 2,9,10,5,7,14,18};
    node* root = build_tree(r4, 10);
    node* c = It_tree_search(root, 12);
    node* x = Tree_maxmum(c);
    node* v = Tree_predecessor(c);
    node* u = Tree_successor(v);
    Tree_delete(root,u);
    node* w1= Tree_successor(v);
}

暫時沒有發現什麼問題,有問題請批評指正,不勝感激!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章