【C++】14.二叉搜索树

1.二叉搜索树

二叉搜索树又称二叉排序树 有两种情况:

一棵空树

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

template<class K>
struct BSTreeNode
{
    BSTreeNode<K>* _left;
    BSTreeNode<K>* _right;
    K _key;

    //new要调构造函数 需要写
    BSTreeNode(const K& key)
        :_left(nullptr)
        ,_right(nullptr)
        ,_key(key)
    {}
};

2.二叉搜索树的查找

//查找
bool Find(const K& key)
{
    Node* cur = _root;
    while (cur)
    {
        if (cur->_key < key)
        {
            cur = cur->_right;
        }
        else if (cur->_key > key)
        {
            cur = cur->_left;
        }
        else
        {
            return true;
        }
    }
    //cur找到空了 说明找不到
    return false;
}

3.二叉搜索树的插入

这里以插入10为例

步骤:

扫描二维码关注公众号,回复: 14587990 查看本文章
  • 1.给到parent和cur两个指针

  • 2.如果cur->_key比插入的小 那么就往左子树走 如果cur->_key比插入的大 就往右子树走

  • 3.直到cur为NULL的时候就可以跳出循环

  • 4.此时cur就为插入的位置 new一个结点 但是你不知道是在你parent的左边还是右边

    所以要用双指针先记录parent的位置 再分两类就可以 如果parent->_key比插入的大

    那就把cur放到parent的左子树 反之就把cur放到右子树

注意:如果树里已经有这个值 那么插入失败 一个二叉搜索树中一个值只出现一次

bool Insert(const K& key)
{
    //如果是空树 给一个节点
    if (_root == nullptr)
    {
        _root = new Node(key);
        return true;
    }
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_key < key)//插入的大 往右走
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_key > key)//插入的小 往左走
        {
            parent = cur;
            cur = cur->_left;
        }
        else//已经有了这个数
        {
            return false;
        }
    }
    cur = new Node(key);//需要与父节点链接到一起 如何处理? 前后指针 记录cur上一个的位置
    if (parent->_key < key)//插入的大 就链接到右边
    {
        parent->_right = cur;
    }
    else//插入的小 就链接到parent的左边
    {
        parent->_left = cur;
    }
    return true;
}

4.二叉搜索树的删除

  • 1.要删除的结点无孩子结点

  • 2.要删除的结点只有左孩子结点(说明该结点右边为空)

  • 3.要删除的结点只有右孩子结点(说明该结点左边为空)

注意:情况1 2 3可能删的是根 那么就不同的情况进行讨论

  • 4.要删除的结点有左、右孩子结点

    节点左右孩子都存在,直接删除不好删除,可以在其子树中找一个替代结点,找其左子树

    中的最大节点,即左子树中最右侧的节点,或者在其右子树中最小的节点,即右子树中最

    小的节点。 替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点。

bool Erase(const K& key)
{
    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_key < key)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_key > key)
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            //如果删根 此时根是左为空 应该把右变为根
            if (cur->_left == nullptr)
            {
                if (cur == _root)
                {
                    _root = cur->_right;
                }
                else
                {
                    //找到了 开始删除
                    //1.左为空
                    //2.右为空
                    //3.左右都不为空

                    if (cur->_left == nullptr)
                    {
                        //看cur原来是parent的左还是右
                        if (parent->_right == cur)
                            parent->_right = cur->_right;
                        else
                            parent->_left = cur->_right;
                    }
                }
                delete cur;
            }
            else if (cur->_right == nullptr)
            {
                //此时cur是根且右为空 那么应该把根给到左边
                if (cur == _root)
                {
                    _root = cur->_left;
                }
                else
                {
                    //看cur原来是parent的左还是右
                    if (parent->_right == cur)
                        parent->_right = cur->_left;
                    else
                        parent->_left = cur->_left;
                }
                delete cur;
            }
            else
            {
                //这里的替换节点一定满足情况1或者情况2 可以删除
                //这里有逻辑问题 删8的时候
                Node* rightMinParent = cur;//nullptr有问题
                Node* rightMin = cur->_right;
                while (rightMin->_left)
                {
                    //左不为空 就往左走
                    rightMinParent = rightMin;
                    rightMin = rightMin->_left;
                }
                //替换
                cur->_key = rightMin->_key;

                //转换成删除rightMin rightMin的左一定为空 所以rightMinParent指向右即可
                //rightMin在左边 父亲的左边指向rightMin的右边
                //rightMin在右边 父亲的右边指向rightMin的右边
                if (rightMin == rightMinParent->_left)
                    rightMinParent->_left = rightMin->_right;
                else
                    rightMinParent->_right = rightMin->_right;
                delete rightMin;
            }
            return true;
        }
    }
    return false;
}

5.插入与删除的测试

void TestBSTree()
{
    BSTree<int> t;
    int a[] = { 5,3,4,1,7,8,2,6,0,9 };
    for (auto e : a)
    {
        t.Insert(e);
    }
    t.InOrder();

    //删7怎么办 解决
    t.Erase(7);
    t.InOrder();

    //1.叶子
    t.Erase(2);
    t.InOrder();

    //2.左为空 或者右为空
    t.Erase(8);
    t.Erase(1);
    t.InOrder();

    //3.左右都不为空(根) 
    t.Erase(5);
    t.InOrder();

    //4.删空也有问题 当根左或者右为空的时候 此时会走把父亲给到左或者右
    //此时根的父亲是空指针 会解引用报错 所以要多一类 必须重置父亲的位置
    for (auto e : a)
    {
        t.Erase(e);
        t.InOrder();
    }
}
#include "BSTree.hpp"

int main()
{
    TestBSTree();
    return 0;
} 

6.二叉搜索树的应用

搜索使用场景:

1.key的模型 判断在不在?

2.key/value的模型 通过key找对应的value?

安检 快递

中英字典互译 计数

搜索效率:如果插入的数据是有序的或者接近有序?那么搜索树效率就完全没办法保障

搜索树的效率最坏的情况下是O(N)

如何解决?

1.AVLTree

2.红黑树

搜索树的修改

搜索树中key是不允许修改的

如果是kv模型的搜索树 可以修改value 但是不能修改key

void TestBSTree()
{
    //匹配
    BSTree<string, string> dict;
    dict.Insert("sort", "排序");
    dict.Insert("string", "字符串");
    dict.Insert("tree", "树");
    dict.Insert("insert", "插入");
    
    string str;
    while (cin >> str)
    {
        BSTreeNode<string, string>* ret = dict.Find(str);
        if (ret)
        {
            cout << ret->_value << endl;
        }
        else
        {
            cout << "无此单词" << endl;
        }
    }
}	

void TestBSTree()
{
    //计数
    string strArr[] = { "西瓜","苹果" ,"西瓜" ,"苹果" ,"西瓜" ,"香蕉" ,"西瓜","樱桃","西瓜","西瓜" };
    BSTree<string, int> countTree;
    for (auto str : strArr)
    {
        BSTreeNode<string, int>* ret = countTree.Find(str);
        if (ret == nullptr)//没有出现过就插入 
        {
            countTree.Insert(str, 1);
        }
        else//再次出现就++
        {
            ret->_value++;
        }
    }
    countTree.InOrder();
}

【C++】14.二叉搜索树 完

猜你喜欢

转载自blog.csdn.net/szh0331/article/details/129865263