zoukankan      html  css  js  c++  java
  • 【数据结构】5.2 二叉搜索树的创建查找以及插入操作

    首先何为二叉搜索树?

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

    二叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构。中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索,插入,删除的复杂度等于树高,O(log(n)).

    结构体定义,为了方便起见,data用int型,读者可以自己更改成模板

    struct BTNode {
        int data;
        BTNode *lchild,*rchild;
    };

    这个没啥好解释的,就是树的结点标准定义,一个数据域,两个孩子指针

    类定义

    class BTree {
    private:
        BTNode *root;                    //二叉搜索树的根结点
        void creat(int loc, BTNode *p); //创建二叉搜索树,递归形式创建,私有函数
        void travel(BTNode *p);            //用来遍历(中序),输出时候需要调用的
        int *data, number;                //这个是用来存储输入的数组,估计不需要了,下一个版本会删除
        void search(int key, BTNode *p, bool &flag);    //查找,其实这个查找方法和二分法类似
        void insert(BTNode *p, int x);        //插入函数
    public:
        BTree(int a[], int n);
        void Creat();
        void Insert(int x);
        void Search(int key);
        void Print();
    };

    本代码采用封装方式,即具体对于root的操作全是由私有函数来进行的,一方面是安全另一方面是代码看起来很整洁

    构造函数

    BTree::BTree(int a[], int n)
    {
        int mid = a[0];        //把第一个数据作为二叉树的根结点的数值
        root = new BTNode();
        root->data = mid;
        root->lchild = root->rchild = NULL;
        number = n;
        data = new int[number];
        for (int i = 0; i < number; i++)
        {
            data[i] = a[i];       
        }
    }

    构造函数只有两个功能,一是创建根结点还有给类中自带的数组拷贝复制

    数组有点多余,因为我当时是先写creat函数的,感觉creat调用的参数有点太多了所以就在类中添加了一个

    完全没必要,直接删掉以后在构造函数中调用creat就行

    查找函数

    void BTree::search(int key, BTNode *p, bool &flag)
    {
        while (p)
        {
            if (p->data == key)        //找到
            {
                flag = true;
                break;
            }
            else if (p->data > key)    //比结点的值小就向左找
            {
                p = p->lchild;
            }
            else                    //比结点的值小就向左找
            {
                p = p->rchild;
            }
        }
    
    }

    和二分法查找类似,不再赘述

    只不过因为查找这里不像前面静态查找中可以直接返回第几个,这里只加了一个flag来进行表示是否查找成功

    可以把函数返回值该成BTNode* 即可返回地址

    插入函数

    void BTree::insert(BTNode *p, int x)
    {
        /*if (p==NULL)
        {
            p = new BTNode();
            p->data = x;
            p->rchild = p->lchild = NULL;
            cout << "p->data" <<p->data<< endl;
        }
        else if (x > p->data)
        {
            insert(p->rchild, x);
        }
        else if (x < p->data)
        {
            insert(p->lchild, x);
        }*/                        //这个错误的原因很明显
        if (x > p->data)
        {
            if (p->rchild == NULL)
            {
                p->rchild = new BTNode();
                p->rchild->data = x;
                p->rchild->rchild = p->rchild->lchild = NULL;
                cout << p->rchild->data << "插入成功!" << endl;
            }
            else
                insert(p->rchild, x);
        }
        else if (x < p->data)
        {
            if (p->lchild == NULL)
            {
                p->lchild = new BTNode();
                p->lchild->data = x;
                p->lchild->rchild = p->lchild->lchild = NULL;
                cout << p->lchild->data << "插入成功!" << endl;
            }
            else
                insert(p->lchild, x);
        }
        else
        {
            cout << "不可以插入已经存在于二叉搜索树中的元素!" << endl;
        }
    
    
    }

    注释那一段可以想一想为什么添加失败/(ㄒoㄒ)/~~

    调了半天才发现原来当p为空的时候再创建已经找不到它的双亲了(孤儿···)

    所以虽然是添加成功了但是在打印族谱 啊 呸 二叉树的时候肯定找不到它了

    因此要么你把双亲的地址提前记下来然后创建好再连接上去,要么就直接用p->lchild/p->rchild来创建

    此处@陈总~感谢提供

    删除功能

    由于实验指导书中并没有要求删除这个功能,因此这里只给出思想,感兴趣的可以自行回去完善。

     二叉查找树的删除,分三种情况进行处理:

      1.p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点)

      2.p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可;(注意分是根节点和不是根节点)

      3.p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点

       具体可以参考: 点我跳转

    完整代码(头文件自己选择添加或者把这个放在头文件里都行)

    struct BTNode {
        int data;
        BTNode *lchild,*rchild;
    };
    class BTree {
    private:
        BTNode *root;                    //二叉搜索树的根结点
        void creat(int loc, BTNode *p); //创建二叉搜索树,递归形式创建,私有函数
        void travel(BTNode *p);            //用来遍历(中序),输出时候需要调用的
        int *data, number;                //这个是用来存储输入的数组,估计不需要了,下一个版本会删除
        void search(int key, BTNode *p, bool &flag);    //查找,其实这个查找方法和二分法类似
        void insert(BTNode *p, int x);        //插入函数
    public:
        BTree(int a[], int n);
        void Creat();
        void Insert(int x);
        void Search(int key);
        void Print();
    };
    void BTree::creat(int loc, BTNode * p)
    {
        if (data[loc] > p->data)    //判断要输入的值是比结点值大还是小,大向右,小向左
        {
            if (p->rchild == NULL)//然后再判断,如果比结点值大且右孩子为空就创建
            {
                p->rchild = new BTNode();
                p->rchild->data = data[loc];
                p->rchild->lchild = p->rchild->rchild = NULL;
            }
            else                  //不为空就继续进行判断比较,递归
            {
                creat(loc, p->rchild);
            }
        }
        else if (data[loc] < p->data)//同理
        {
            if (p->lchild == NULL)
            {
                p->lchild = new BTNode();
                p->lchild->data = data[loc];
                p->lchild->lchild = p->lchild->rchild = NULL;
            }
            else
            {
                creat(loc, p->lchild);
            }
    
        }
    }
    void BTree::travel(BTNode * p)
    {    
        //中序遍历,左根右
        if (p != NULL)
        {
            travel(p->lchild);//先左
            cout << p->data << " ";//
            travel(p->rchild);//后右
        }
    }
    void BTree::search(int key, BTNode *p, bool &flag)
    {
        while (p)
        {
            if (p->data == key)        //找到
            {
                flag = true;
                break;
            }
            else if (p->data > key)    //比结点的值小就向左找
            {
                p = p->lchild;
            }
            else                    //比结点的值小就向左找
            {
                p = p->rchild;
            }
        }
    
    }
    void BTree::insert(BTNode *p, int x)
    {
        /*if (p==NULL)
        {
            p = new BTNode();
            p->data = x;
            p->rchild = p->lchild = NULL;
            cout << "p->data" <<p->data<< endl;
        }
        else if (x > p->data)
        {
            insert(p->rchild, x);
        }
        else if (x < p->data)
        {
            insert(p->lchild, x);
        }*/                        //这个错误的原因很明显
        if (x > p->data)
        {
            if (p->rchild == NULL)
            {
                p->rchild = new BTNode();
                p->rchild->data = x;
                p->rchild->rchild = p->rchild->lchild = NULL;
                cout << p->rchild->data << "插入成功!" << endl;
            }
            else
                insert(p->rchild, x);
        }
        else if (x < p->data)
        {
            if (p->lchild == NULL)
            {
                p->lchild = new BTNode();
                p->lchild->data = x;
                p->lchild->rchild = p->lchild->lchild = NULL;
                cout << p->lchild->data << "插入成功!" << endl;
            }
            else
                insert(p->lchild, x);
        }
        else
        {
            cout << "不可以插入已经存在于二叉搜索树中的元素!" << endl;
        }
    
    
    }
    BTree::BTree(int a[], int n)
    {
        int mid = a[0];        //把第一个数据作为二叉树的根结点的数值
        root = new BTNode();
        root->data = mid;
        root->lchild = root->rchild = NULL;
        number = n;
        data = new int[number];
        for (int i = 0; i < number; i++)
        {
            data[i] = a[i];        //创建这个数组个人觉得有点多余,其实可以直接在构造函数里完成
                                //自己改一下吧,直接在构造函数里调用creat,参数需要调整一下
        }
    }
    void BTree::Insert(int x)
    {
        BTNode *p = root;
        insert(p, x);
        //这里可以把insert函数增加一个参数bool类型来判断是否真的是插入成功了
        //插入成功会有提示,失败不会打印插入成功的消息
    }
    void BTree::Search(int key)
    {    
        //此函数对应私有函数没有返回值,可以自己更改,把地址取出来(目前没有用所以我只是做了一个简单的判断)
        BTNode *p = root;
        bool flag = false;
        search(key, p, flag);
        if (flag)                
        {
            cout << "搜索到" << key << "在二叉树中" << endl;
        }
        else
        {
            cout << "未查找到指定数据!" << endl;
        }
    }
    void BTree::Print()
    {
        BTNode *p = root;
        travel(p);
        cout << endl;
    
    }
    void BTree::Creat()
    {
            BTNode *p = root;
            for (int i = 1; i < number; i++)    //从1开始是因为root在构造函数中已经被赋值了
            {
                creat(i, p);
            }
            cout << "二叉搜索树创建成功了。。。吧" << endl;        
    }
    View Code

    主函数代码

    int main()
    {
        cout << "请输入二叉搜索树的元素个数:";
        int number, *a, key, x;
        cin >> number;
        a = new int[number];
        cout << "请分别为这些元素赋值:" << endl;
        for (int i = 0; i < number; i++)
        {
            cin >> a[i];
        }
        BTree test(a, number);
        test.Creat();
        test.Print();
        cout << "请输入要查找的数值" << endl;
        cin >> key;
        test.Search(key);
        cout << "请输入要插入的数值" << endl;
        cin >> x;
        test.Insert(x);
        test.Print();
        system("pause");
        return 0;
    }
    View Code

    测试截图

  • 相关阅读:
    聊聊简单又灵活的权限设计(RBAC)
    手把手搭建一个属于自己的在线 IDE
    聊一聊如何搭建高性能网站哪一些事
    一个老程序员的忠告:你这辈子输就输在以为靠技术就能生存下
    缓存提升性能的关键性手段
    python学习笔记1之-python简介及其环境安装
    聊一聊mycat数据库集群系列之双主双重实现
    mycat数据库集群系列之mycat读写分离安装配置
    mycat数据库集群系列之mysql主从同步设置
    mycat数据库集群系列之数据库多实例安装
  • 原文地址:https://www.cnblogs.com/robotpaul/p/10152476.html
Copyright © 2011-2022 走看看