【数据结构X.9】二叉排序树(BST)的插入、查找(非递归),递归算法、中序遍历二叉排序树,输出有序树,层序遍历BST树、创建一颗二叉排序树、二叉排序树的删除

32 篇文章 0 订阅
16 篇文章 1 订阅

主题:

代码实现:二叉排序树(BST)的插入、查找(非递归),递归算法、中序遍历二叉排序树,输出有序树,层序遍历BST树、创建一颗二叉排序树、二叉排序树的删除等等。

概念:

没什么好说的,二叉排序树(BST)就是一个中序遍历有序的树,如图中的红色的区域左边整体小于右边,以77为根,明显绿色区域小于黄色区域,
所以进行插入时,遇到了比它的根节点大的,就放在左边,比它的根节点小的就放在右边,然后递归,如果遇到了合适的空位,也就是tr==NULL这时候就是插入的时候,
在这里插入图片描述

难点:

二叉排序树(BST)最难的地方在于其删除操作

  1. 当是需要删除的节点是叶子节点,可以直接删掉,因为它没有左右孩子,不需要考虑删除后,二叉排序树的性质改变。
  2. 当需要删除的节点只有一个孩子时,这个孩子可以是左孩子,也可以是右孩子;需要删除当前节点,然后把左孩子(或者右孩子)接到这个被删除节点原本的位置上,也就是被删除节点的父节点的对应孩子变成了被删除节点的孩子,可以理解为【儿子谋权篡位,杀了爸爸,然后继承了爷爷的皇位】 。——————这个从字面上看,需要找到被删除节点的父节点————————而一个巧妙的实现是,交换父节点和儿子节点的值,然后删除儿子节点,并把儿子节点的儿子节点接到原本的父节点的位置。(本文使用的是查找父节点的方式,便于理解)
  3. 当需要删除的节点有两个孩子时,删除该节点时,左边都是比该节点小的值,右边都是比该节点大的值,所以需要找到一个合适的值【接替皇位】,这个值可以是正好比该节点小的值(也就是中序遍历的前驱节点)【皇帝的弟弟】,也可以是正好比该节点大的值【皇帝的哥哥】(也就是中序遍历的,后继节点)。这个值作为根节点,显然,这个根节点左边的值都比根节点小,右边的值都比根节点大。————为了实现这个目标,需要一个查找后继节点的函数,并且需要删除原节点,且把接替它的新的根【新皇帝】节点接上去【顶替皇位】。——————一个巧妙的实现方法同上2:交换要删除的值和顶替这个被删除的节点值的值;然后删除这个旧值的节点,这样一来,就转化为了(1或者2或者3)
例如:

删除77,87和77交换位置,而删除77的操作是情况2,需要删除的节点只有一个孩子,这样一来,就可以递归地删除,77的孩子顶替77,
在这里插入图片描述
在这里插入图片描述
得到结果:
在这里插入图片描述

输入:

在这里插入图片描述

5
77
12
87
99
43
123
0

函数调用:

BSTree_use.cpp
#include "BSTree.h"
int main()
{
    int key = 5;
    BSTree tr = CreateBSTree();
    std::cout << "请输入需要查找的值:";
    std::cin >> key;

    std::cout << "二叉排序树查找" << key << "(非递归)" << BST_Search(tr, key) << std::endl;
    std::cout << "二叉排序树查找" << key << "(递归)" << FindNodeK(tr, key) << std::endl;

    DeleteBSTreeNode(tr, 77);
    DeleteBSTreeNode(tr, 5);
    DeleteBSTreeNode(tr, 12);
    DeleteBSTreeNode(tr, 87);
    DeleteBSTreeNode(tr, 99);
    DeleteBSTreeNode(tr, 123);
    DeleteBSTreeNode(tr, 43);
}

函数定义:

BSTree.h
#include <iostream>
#include <stack>
#include <queue>
typedef struct BSNode
{
    int data;
    struct BSNode *lchild, *rchild;
} * BSTree, BSNode;

/***
 * 二叉排序树(BST)的插入
 * 成功返回1, 失败返回0
 */
int BSTreeInsert(BSTree &tr, int k)
{
    if (!tr)
    {
        tr = (BSNode *)malloc(sizeof(BSNode));
        tr->data = k;
        tr->lchild = tr->rchild = NULL;
        return 1;
    }
    else if (k == tr->data)
    { //已经有了这个节点了
        return 0;
    }
    else if (k < tr->data)
    {
        return BSTreeInsert(tr->lchild, k);
    }
    else
    {
        return BSTreeInsert(tr->rchild, k);
    }
}

/***
 * 二叉排序树的查找(非递归)
 */
BSNode *BST_Search(BSTree tr, int k)
{
    while (tr && k != tr->data)
    {
        if (k < tr->data)
            tr = tr->lchild;
        else
        {
            tr = tr->rchild;
        }
    }
    return tr;
}

/***
 * 二叉排序树(BST)的查找,递归算法
 */
BSNode *FindNodeK(BSTree tr, int k)
{
    if (!tr)
        return NULL;
    FindNodeK(tr->lchild, k);
    if (tr->data == k)
        return tr;
    FindNodeK(tr->rchild, k);
}

/**
 * 中序遍历二叉排序树,输出有序树
 */
void VisitBSTree(BSTree tr)
{
    if (tr)
    {
        VisitBSTree(tr->lchild);
        std::cout << tr->data << " ";
        VisitBSTree(tr->rchild);
    }
}
/**
 * 层序遍历BST树
 */
void VisitLevelBSTree(BSTree tr)
{
    std::queue<BSNode *> q;
    BSNode *p = tr, *r = tr;
    q.push(p);
    while (!q.empty())
    {
        p = q.front();
        q.pop();
        if (p)
        {
            std::cout << p->data << " ";
            q.push(p->lchild);
            q.push(p->rchild);
            if (p == r)
            {
                std::cout << std::endl;
                r = q.back();
            }
        }
    }
}

/**
 * 创建一颗二叉树
 */
BSTree CreateBSTree()
{
    int input = 5;
    BSTree tr = NULL;

    std::cout << "请输入树的节点值,输入0结束: ";
    std::cin >> input;
    while (input)
    {
        std::cout << BSTreeInsert(tr, input) << " "
                  << "目前层序遍历BST树序列:\n";
        VisitLevelBSTree(tr);
        std::cout << std::endl;
        std::cout << "请输入树的节点值,输入0结束: ";
        std::cin >> input;
    }
    return tr;
}

/***
 * 二叉排序树的删除
 * 1.如果是叶子节点x,直接删除
 * 2.如果x只有左儿子,或者只有右儿子,让子树成为被删除节点x的字数,替代x的位置
 * 3.节点有左、右两颗子树,令x的后继节点(或者前驱节点)替代x,
 *    然后从二叉树删除这个后记节点(或者前驱节点)
 * 所以需要额外写一个查找x的后继节点(或者前驱节点)的方法
 */
/***
 * 查找节点x的父亲节点的函数
 */
BSNode *FindNodeFather(BSTree tr, int x)
{
    std::stack<BSNode *> s;
    BSNode *p = tr;
    while (p || !s.empty())
    {
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            s.pop();
            if ((p->lchild && p->lchild->data == x) || (p->rchild && p->rchild->data == x))
                return p;
            p = p->rchild;
        }
    }
    return NULL;
}

/**
 * 查找BST二叉树的直接后继
 */
BSNode *FindDirectChild(BSTree tr, int x)
{
    int next = 0;
    std::stack<BSNode *> s;
    BSNode *p = tr;
    while (p || !s.empty())
    {
        if (next && p)
            return p;
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            s.pop();

            if (p->data == x)
                next = 1;
            p = p->rchild;
        }
    }
    return NULL;
}

bool DeleteBSTreeNode(BSTree &tr, int x)
{
    if (!tr)
        return false; //空树,还删除p =。=

    BSNode *p = tr;
    std::stack<BSNode *> s;
    while (!s.empty() || p)
    {
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            if (p->data == x) //找到了这个节点
            {
                if (!p->lchild && !p->rchild) //是叶子节点,直接删除
                {
                    std::cout << "删除叶子节点 " << x << std::endl;
                    BSNode *tmp = FindNodeFather(tr, x);
                    if (!tmp)
                    {
                        //返回了空值,说明没有父节点,这个叶子节点是根节点
                        tr = NULL;
                        return true;
                    }
                    else
                    {
                        if (tmp->lchild && tmp->lchild == p)
                            tmp->lchild = NULL;
                        else if (tmp->rchild && tmp->rchild == p)
                            tmp->rchild = NULL;
                        else
                        {
                            std::cout << "-----代码写错了,删除叶子出现了不可能出现的情况-----";
                        }
                    }
                    free(p);
                    VisitBSTree(tr);
                    std::cout << std::endl;
                    return true;
                }
                else if (p->lchild && p->rchild)
                {
                    std::cout << "删除双孩子节点 " << x << std::endl;
                    BSNode *t = FindDirectChild(tr, x);
                    if (t)
                    {
                        std::cout << "找到" << x << "后继节点:" << t->data << std::endl;
                        int deleteNum = 1000000;
                        p->data = t->data;
                        t->data = deleteNum;
                        DeleteBSTreeNode(tr, deleteNum);
                    }
                    else
                    {
                        std::cout << "-----代码写错了,删除双孩子出现了不可能出现的情况-----";
                    }
                    return false;
                }
                else if (!p->lchild || !p->rchild)
                {

                    std::cout << "删除单孩子节点 " << x << std::endl;
                    BSNode *child;
                    if (p->lchild)
                        child = p->lchild;
                    else
                        child = p->rchild;
                    BSNode *tmp = FindNodeFather(tr, x);
                    if (!tmp)
                    {
                        //返回了空值,说明没有父节点,这个单孩子节点是根节点
                        tr = child;
                        std::cout << "删除根孩子节点,更新根节点为: " << tr->data << std::endl;
                    }
                    else
                    {
                        if (tmp->lchild && tmp->lchild == p)
                            tmp->lchild = child;
                        else if (tmp->rchild && tmp->rchild == p)
                            tmp->rchild = child;
                        else
                        {
                            std::cout << "-----代码写错了,删除单孩子出现了不可能出现的情况-----";
                        }
                    }
                    free(p);
                    VisitBSTree(tr);
                    std::cout << std::endl;
                    return true;
                }
                else
                {
                    std::cout << "-----代码写错了,删除出现了不可能出现的情况-----";
                }
                return false;
            }
            s.pop();
            p = p->rchild;
        }
    }
    return false; //没有这个节点,直接返回了
}

碎碎念

实现大概花了4个小时,比较慢,有些地方实在不能立刻理解,好在最后全部实现了 = = 。

写得不错的参考:

动态规划 - 最优二叉搜索树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值