C++数据结构——二叉搜索树(详细图解,详细代码实现)


一: 🔥二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

二: 🔥 二叉搜索树操作

在这里插入图片描述

  1. 二叉搜索树的查找
  • a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  • b、最多查找高度次,走到到空,还没找到,这个值不存在。
  1. 二叉搜索树的插入
  • a. 树为空,则直接新增节点,赋值给root指针
  • b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
    在这里插入图片描述
  1. 二叉搜索树的删除
    首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
    a. 要删除的结点无孩子结点
    b. 要删除的结点只有左孩子结点
    c. 要删除的结点只有右孩子结点
    d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除

在这里插入图片描述

三: 🔥 二叉搜索树的实现

完整代码如下:

#include<iostream>
using namespace std;
 
template<class T>
struct BSTNode
{
    BSTNode(const T& data = T())
        :
        _pLeft(nullptr), _pRight(nullptr), _data(data)
    {}
    BSTNode<T>* _pLeft;
    BSTNode<T>* _pRight;
    T _data;
};
template<class T>
class BSTree
{
    typedef BSTNode<T> Node;
    typedef Node* PNode;
public:
    BSTree() : _pRoot(nullptr)
    {}
    ~BSTree(){
        destory(_pRoot);
        _pRoot = nullptr;
    }
    PNode Find(const T& data)
    {
        Node* cur = _pRoot;
        while (cur != nullptr)
        {
            if (data < cur->_data)
                cur = cur->_pLeft;
            else if (data > cur->_data)
                cur = cur->_pRight;
            else
                return cur;
        }
        return nullptr;
    }
    bool Insert(const T& data)
    {
        // 如果树为空,直接插入
        if (nullptr == _pRoot)
        {
            _pRoot = new Node(data);
            return true;
        }
        // 按照二叉搜索树的性质查找data在树中的插入位置
        PNode pCur = _pRoot;
        // 记录pCur的双亲,因为新元素最终插入在pCur双亲左右孩子的位置
        PNode pParent = nullptr;
        while (pCur)
        {
            pParent = pCur;
            if (data < pCur->_data)
                pCur = pCur->_pLeft;
            else if (data > pCur->_data)
                pCur = pCur->_pRight;
            else
                return false; // 元素已经在树中存在
        }
        // 插入元素
        pCur = new Node(data);
        if (data < pParent->_data)
            pParent->_pLeft = pCur;
        else
            pParent->_pRight = pCur;
        return true;
        /*InsertR(_pRoot,data);
        return true;*/
    }
    bool Erase(const T& data)
    {
        // 如果树为空,删除失败
        if (nullptr == _pRoot)
            return false;
        // 查找在data在树中的位置
        PNode pCur = _pRoot;
        PNode pParent = nullptr;
        while (pCur)
        {
            if (data == pCur->_data)
                break;
            else if (data < pCur->_data)
            {
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else
            {
                pParent = pCur;
                pCur = pCur->_pRight;
            }
        }
        // data不在二叉搜索树中,无法删除
        if (nullptr == pCur)
            return false;
        // 分以下情况进行删除
        if (nullptr == pCur->_pRight)
        {
            // 当前节点只有左孩子或者左孩子为空---可直接删除
            if (pParent->_pLeft == pCur)
                pParent->_pLeft = pCur->_pLeft;
            else
                pParent->_pRight = pCur->_pLeft;
            delete pCur;
        }
        else if (nullptr == pCur->_pLeft)
        {
            // 当前节点只有右孩子---可直接删除
            if (pParent->_pLeft == pCur)
                pParent->_pLeft = pCur->_pRight;
            else
                pParent->_pRight = pCur->_pRight;
            delete pCur;
        }
        else
        {
            Node* leftmax = pCur->_pLeft;
            Node* pre = pCur;
            while (leftmax->_pRight) {
                pre = leftmax;
                leftmax = leftmax->_pRight;
            }
            pCur->_data = leftmax->_data;
            if (leftmax->_pLeft)
                pre->_pRight = leftmax->_pLeft;
            else if (pre == pCur)
                pCur->_pLeft = nullptr;
            else
                pre->_pRight = nullptr;
            delete leftmax;
            leftmax = nullptr;
        }
        return true;
        //EraseR(_pRoot, data);
        //return true;
    }
    void InOrder()
    {
        print(_pRoot);
        cout << endl;
    } 
private:
    PNode _pRoot;
    void InsertR(PNode& node, const T& data)
    {
        if (nullptr == node) {
            node = new Node(data);
            return;
        }
        if (node->_data == data)
            return;
        if (data < node->_data)
            InsertR(node->_pLeft, data);
        else
            InsertR(node->_pRight, data);
        return;
    }
    PNode EraseR(PNode& node, const T& data)
    {
        if (nullptr == node)
            return node;
        if (data == node->_data)
        {
            if (!node->_pRight)
                return node->_pLeft;
            else if (!node->_pLeft)
                return node->_pRight;
            else {
                if (!node->_pRight) {
                    PNode temp = node->_pLeft;
                    delete node;
                    return temp;
                }
                else if (!node->_pLeft) {
                    PNode temp = node->_pRight;
                    delete node;
                    return temp;
                }
                else {
                    PNode minNode = FindMin(node->_pRight);
                    node->_data = minNode->_data;
                    node->_pRight = EraseR(node->_pRight, minNode->_data);
                }
            }
        }
        if (data < node->_data)
            node->_pLeft = EraseR(node->_pLeft, data);
        else if (data > node->_data)
            node->_pRight = EraseR(node->_pRight, data);
        return node;
    }
    PNode FindMin(PNode node) const {
        while (node && node->_pLeft != nullptr)
            node = node->_pLeft;
        return node;
    }
    void destory(const PNode& node)
    {
        if (node == nullptr)
            return;
        destory(node->_pLeft);
        destory(node->_pRight);
        delete node;
    }
    void print(PNode node)
    {
        if (node == nullptr)
            return;
        print(node->_pLeft);
        cout << node->_data << " ";
        print(node->_pRight);
    }
};

四: 🔥二叉搜索树的性能分析

  • 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能

  • 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

  • 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
    在这里插入图片描述

  • 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

  • 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续章节学习的AVL树和红黑树就可以上场了

以上就是二叉搜索树的图解与完整实现过程,欢迎在评论区留言,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值