zoukankan      html  css  js  c++  java
  • 二叉搜索树的相关操作

    操作包括二叉搜索树的创建,插入,搜索,寻找前驱后继,删除,左右旋转,插入元素为根结点,以及两棵二叉树的合并。

    二叉树的创建很简单,只需要设置 value, left child, right child 即可。

    插入的时候递归插入树中合适的位置,通过比较插入元素的值与根结点元素的值,如果小于则递归插入到左子树中,否则递归插入到右子树中。

    搜索的时候与插入类似,比较要搜索的值和根结点元素值的大小,若小于则递归到左子树中去查找,否则递归到右子树中去查找。

    寻找前驱的方式是在左子树的右结点中去递归寻找,一直找到右结点的右子结点为空,则该右结点即是前驱,寻找后继是在右子树的左结点中去递归寻找,一直找到左结点的左子结点为空,则该左结点即是后继。

    结点的删除有些复杂,如果删除的叶结点,则直接删除,否则就要用该结点的前驱中的值来代替该结点中的值,然后递归删除前驱,若不存在前驱,则使用该结点的后继来代替该结点中的值,然后递归删除后继,如果后继也不存在,证明该结点是个叶子结点,直接删除即可。

    左旋的方式是让根结点的右结点变成新的根结点,再让新的根结点的左结点指向原来的根结点,再让原来根结点的右结点指向新的根结点原来的左结点,这样操作之后就好像原来的根结点向左旋转了一样。

    右旋的操作正好和左旋是对称的,将根结点的左结点变成新的根结点,再让新的根结点的右结点指向原来的根结点,再让原来根结点的左结点指向新的根结点原来的右结点,这样操作之后就好像根结点右旋了一样。

    原来的插入操作是直接插入到叶子结点上的,而插入一个元素使其变为根结点就需要旋转操作来实现,如果插入元素大于根结点值,则递归插入到右子树中让其成为右子树的根结点,再将树左旋即可实现,若插入元素小于根结点值,则递归插入到左子树中让其成为左子树中的根结点,再将树右旋即可,递归到最后必然到叶子结点上,此时用插入元素的值来创建一个新的结点,之后依次回程旋转即可实现。

    要合并两棵二叉树,基本的思路是遍历其中一棵树,然后递归插入到另一棵树中,优化方式是先将其中一棵树A的根结点插入到另外一棵树B中,此时两棵树的根结点相同,此后树A的左子树插入到树B中必然也是在左子树中,右子树也是同样。所以就可以递归将A的左结点插入到B的左子树中,将A的右结点递归插入到B的右子树中,注意因为这种方式依赖于左右子树和根结点的大小关系,所以只能采用前序递归方式,否则合并之后会破坏树中元素的先后顺序。

    具体详见代码:

    //BST.cpp
    
    #include <malloc.h>
    #include <iostream>
    #include "binary_tree.h"//defined BST struct
    #include "print_binary_tree.h"
    //the node of a tree, included in binary_tree.h
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    
    //create node with value, left child and right child 
    struct bst_node* create_bst_node(int value, struct bst_node* l, struct bst_node* r){
        struct bst_node* t =(struct bst_node*) malloc(sizeof *t);//should call free() when finished
        t->value = value;
        t->l = l;
        t->r = r;
        return t;
    }
    
    //bst insert
    struct bst_node* bst_insert(struct bst_node* root, int value){
        if(root == NULL) 
            return create_bst_node(value,NULL,NULL);//tree is empty
        //if root is not NULL
        if(value < root->value)
            root->l = bst_insert(root->l,value);//insert into left child
        else if (value > root->value)
            root->r = bst_insert(root->r,value);//
        return root;
    }
    
    //bst search, the keyword "struct" can be omitted
    struct bst_node* bst_search(struct bst_node* bst_root, int value){
        if(bst_root == NULL)
            return NULL;
        if(value < bst_root->value){
            return bst_search(bst_root->l, value);
        }else if(value > bst_root->value){
            return bst_search(bst_root->r, value);
        }
        return bst_root;//neither greater nor less, must be equal
    }
    //predecessor 
    bst_node* predecessor(bst_node* t){
        t = t->l;
        while(t->r != NULL){
            t = t->r;
        }
        return t;
    }
    //successor 
    bst_node* successor(bst_node* t){
        t = t->r;
        while(t->l != NULL){
            t = t->l;
        }
        return t;
    }
    //bst remove
    bst_node* bst_remove(bst_node* t, int value){
        if(t == NULL) return NULL;
        if(value < t->value){
            t->l = bst_remove(t->l,value);//return a new left subtree which has removed the value
        }else if(value > t->value){
            t->r = bst_remove(t->r,value);//return a new right subtree which has removed the value
        }else{//have found the value
            if(t->l != NULL){//get the predecessor first, then remove predecessor in left subtree
                t->value = predecessor(t)->value;
                t->l = bst_remove(t->l,t->value);
            }else if(t->r != NULL){//get the successor first, then remove successor in right subtree
                t->value = successor(t)->value;
                t->r = bst_remove(t->r,t->value);
            }else{
                //now t is a leaf node,and remove it directly
                free(t);
                t = NULL;
            }
        }
        return t;
    }
    
    //bst rotate
    //bst rotate right, return left child
    bst_node* bst_rot_r(bst_node* t){
        if(t == NULL || t->l ==NULL) return t;
        else{
            bst_node* tl = t->l;
            t->l = tl->r;
            tl->r = t;
            return tl;
        }
    }
    //bst rotate left, return right child
    bst_node* bst_rot_l(bst_node* t){
        if(t == NULL || t->r ==NULL) return t;
        else{
            bst_node* tr = t->r;
            t->r = tr->l;
            tr->l = t;
            return tr;
        }
    }
    //insert node to root
    bst_node* bst_insert_to_root(bst_node* t, int value){
        if(t == NULL)//insert to leaf 
            return create_bst_node(value,NULL,NULL);
        if(value > t->value){
            t->r = bst_insert_to_root(t->r,value);
            return bst_rot_l(t);
        }else if(value < t->value){
            t->l = bst_insert_to_root(t->l,value);
            return bst_rot_r(t);
        }else{
            return t;
        }
    }
    
    //merge two trees
    bst_node* bst_merge_mine(bst_node* a, bst_node* b)
    {
        if (b==NULL) return a;
        if (a==NULL) return b;
        
        //get the value from a, and insert the value into b
        //我是直接从根结点开始插入
        //我的算法前序递归和后序递归都是可以的
        //中序递归会生成一颗倾斜的树,应为中序遍历结点的值往后依次是增大的
        //最后的一个元素是最大的,又成为了根结点,所以合成树是向右上方倾斜的
        b = bst_merge_mine(a->l,b);
        b = bst_merge_mine(a->r,b);
        b = bst_insert_to_root(b,a->value);//let root of a tobe the root of merged tree
        
        return b;
    }
    
    //merge two trees
    //基本思想还是遍历a中的元素,递归插入到b中
    //但是这个比我的算法稍微好的地方在于将a和b的根结点设置为一样之后
    //每次插入可以不从b的根结点开始,因为此时a的左树插入之后必然在合并之树的左边,
    //a的右子树插入之后也必然在合并之树的右边,所以插的时候a的左子树可以直接插到b的左子结点去,
    //同理a的右结点可以插到b的右子树中去
    bst_node* bst_merge(bst_node* a, bst_node* b)
    {//整个递归过程是前序递归:root->left child->right child
        //从一开始想到这两个条件并不困难,
        //但是在递归的时候这两个条件却起到了很大作用
        //这两个条件配合下面的左右子树赋值语句b->l=... b->r=...发挥了很妙的作用
        if (a==NULL) return b;//
        if (b==NULL) return a;//这个条件很微妙,在递归的时候表现的很巧妙
        //注意,因为此处利用了左右子树和根的相互关系,所以只能是前序递归
        //中序递归和后序递归都会破坏树中元素的先后关系
        b = bst_insert_to_root(b,a->value);//set a to root
        b->l = bst_merge(a->l, b->l);//将a的左结点取出,插到b的左子树中去
        b->r = bst_merge(a->r, b->r);//将a的右结点取出,插到b的右子树中去
        free(a);//每次合并完后将a的子树释放
        a = NULL;
        return b;
    }
    
    
    int main(){
        //create_bst_node();
        //srand((unsigned)time(NULL));//使用系统时间初试化随机种子
        int value = rand()%123;
        bst_node* bst_root = create_bst_node(value, NULL, NULL);
        //for(int k=0; k<39; k++){
        for(int k=0; k<9; k++){
            value = rand()%123;
            bst_insert(bst_root, value);
        }
    
        print_binary_tree(bst_root);
    
        value = rand()%123;
        bst_node* bst_root2 = create_bst_node(value, NULL, NULL);
        //for(int k=0; k<39; k++){
        for(int k=0; k<9; k++){
            value = rand()%123;
            bst_insert(bst_root2, value);
        }
    
        print_binary_tree(bst_root2);
        bst_node* m = bst_merge(bst_root,bst_root2);
        print_binary_tree(m);
        return 0;
    }
    

    去吧,去吧,到彼岸去吧,彼岸是光明的世界!
  • 相关阅读:
    消息模型:主题和队列的区别
    Navicat12 无限试用(Windows64、Linux、Mac)
    中文技术文档的写作规范
    CentOS7安装GitLab和Jenkins构建CICD环境
    使用Ansible自动化安装MySQL数据库
    CentOS7 Linux生产环境源码编译离线安装Ansible自动化运维工具
    配置Prometheus+Grafana监控主机节点、MySQL、Node、Redis、Mongod、Nginx等
    读书分享——Beyond Feelings: A Guide to Critical Thinking
    Linux云计算架构师及大数据自学资料分享(全集)
    CentOS7安装Rancher企业容器平台
  • 原文地址:https://www.cnblogs.com/lengyue365/p/5090225.html
Copyright © 2011-2022 走看看