【算法导论】二叉搜索树的实现

    • 二叉搜索树的实现
      • 1.首先给出二叉树的结构体
      • 2.查询操作
      • 3. 找出子树的最大值和最小值
      • 4.节点的前驱和后继
      • 5.插入
      • 6.删除
      • 7构建树
      • 8.测试过程

二叉搜索树的实现

二叉搜索树的特点是, A.left<=A<=A.right A . l e f t <= A <= A . r i g h t 。对于一个节点来说,左子树是比该节点小的,右子树是比该节点大的。

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);
}

暂时没有发现什么问题,有问题请批评指正,不胜感激!

你可能感兴趣的:(算法导论)