C++进阶 二叉搜索树的讲解

在这里插入图片描述

二叉搜索树的概念

二叉搜索树又称为二叉排序树。

  • 二叉搜索树的性质
    • 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值
    • 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值
    • 它的左右子树也分别为二叉搜索树
    • 二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值。

二叉搜索树的插入

    1. 树为空,则直接新增结点,赋值给 root 指针
    1. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。
    1. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走)

例子:

int a[] = {8, 3, 1, 10, 1 6, 4, 7, 14, 13};

请添加图片描述

代码:

bool Insert(const K &key, const V &value)
{
    if (_root == nullptr)
    {
        _root = new Node(key, value);
        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, value);
    if (parent->_key < key)
    {
        parent->_right = cur;
    }
    else
    {
        parent->_left = cur;
    }

    return true;
}

二叉搜索树的查找

    1. 从根开始比较,查找 x,x 比根的值大则往边走查找,x 比根值小则往边走查找。
    1. 最多查找高度次,走到到空,还没找到,这个值不存在。
    1. 如果不支持插入相等的值,找到 x 即可返回。
    1. 如果支持插入相等的值,意味着有多个 x 存在,一般要求查找中序的第一个 x

代码:

Node *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 cur;
        }
    }

    return nullptr;
}

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回 false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为 N)

  1. 要删除结点 N 左右孩子均为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向空,直接删除 N 结点
  2. 要删除的结点 N 左孩子位空,右孩子结点不为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向 N 的右孩子,直接删除 N 结点
  3. 要删除的结点 N 右孩子位空,左孩子结点不为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向 N 的左孩子,直接删除 N 结点
  4. 要删除的结点 N 左右孩子结点均不为空
    • 解决方法:无法直接删除 N 结点,因为 N 的两个孩子无处安放,只能用替换法删除。**找 N 左子树的值最大结点 R(最右结点)**或者 N 右子树的值最小结点 R(最左结点)替代 N,因为这两个结点中任意一个,放到 N 的位置,都满足二叉搜索树的规则。替代 N 的意思就是 N 和 R 的两个结点的值交换,转而变成删除 R 结点,R 结点符合情况 2 或情况 3,可以直接删除。

代码:

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
                {
                    if (parent->_left == cur)//如果父节点的左节点是当前节点
                    {
                        parent->_left = cur->_right;//将父节点的左节点连接至当前节点的右子节点。
                    }
                    else//如果父节点的右节点是当前节点
                    {
                        parent->_right = cur->_right;//将父节点的右节点连接至当前节点的右子节点。
                    }
                }
                delete cur;//删除当前节点
            }
            else if (cur->_right == nullptr)//右节点为空
            {
                if (cur == _root)//如果是根节点,就将根节点的左子节点设置为根节点
                {
                    _root = cur->_left;
                }
                else
                {
                    if (parent->_left == cur)//如果父节点的左节点是当前节点
                    {
                        parent->_left = cur->_left;//将父节点的左节点连接至当前节点的左子节点。
                    }
                    else//如果父节点的右节点是当前节点
                    {
                        parent->_right = cur->_left;//将父节点的右节点连接至当前节点的左子节点。
                    }
                }

                delete cur;//删除当前节点
            }
            else
            {
                // 左右节点都不为空
                // 这里默认替换右子树最左节点c
                Node *replaceParent = cur;
                Node *replace = cur->_right;//进入根节点的右子树
                while (replace->_left)//迭代左节点,找到右子树的最左节点
                {
                    replaceParent = replace;//记录父节点
                    replace = replace->_left;//迭代左节点
                }
                //此时替换节点(replace)的左节点一定为空
                cur->_key = replace->_key;//交换值

                if (replaceParent->_left == replace)//如果替换节点是左节点
                    replaceParent->_left = replace->_right;//将父节点的左赋值为替换节点的右,因为替换节点的左节点一定是空的,因为是按照根节点的右子树的最左节点查找的。
                else//如果替换节点是右节点
                    replaceParent->_right = replace->_right;//将父节点的右赋值为替换节点的右,因为替换节点的左节点一定是空的,因为是按照根节点的右子树的最左节点查找的。

                delete replace;//删除替换节点
            }

            return true;
        }
    }

    return false;
}

二叉搜索树的析构

  • 递归析构
public:
~BSTree()
{
    Destroy(_root);
    _root = nullptr;
}

private:
void Destroy(Node *root)
{
    if (root == nullptr)
        return;

    Destroy(root->_left);
    Destroy(root->_right);
    delete root;
}


二叉搜索树的深拷贝

BSTree(const BSTree &t)
{
    _root = Copy(t._root);
}

Node *Copy(Node *root)
{
    if (root == nullptr)
        return nullptr;

    Node *newRoot = new Node(root->_key, root->_value);
    newRoot->_left = Copy(root->_left);
    newRoot->_right = Copy(root->_right);
    return newRoot;
}

二叉搜索树的=重载

BSTree &operator=(BSTree tmp)
{
    swap(_root, tmp._root);//tmp为拷贝,可以直接交换,不影响tmp
    return *this;
}

完整代码

#pragma once
#include <iostream>
using namespace std;

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

    BSTNode(const K &key)
        : _key(key), _left(nullptr), _right(nullptr)
    {
    }
};

namespace key
{

    template <class K>
    class BSTree
    {
        typedef BSTNode<K> Node;

    public:
        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);
            if (parent->_key < key)
            {
                parent->_right = cur;
            }
            else
            {
                parent->_left = cur;
            }

            return true;
        }

        void Inorder() // 中序遍历
        {
            _Inorder(_root);
        }

        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;
                }
            }
            return false;
        }

        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
                        {
                            if (parent->_left == cur)
                            {
                                parent->_left = cur->_right;
                            }
                            else
                            {
                                parent->_right = cur->_right;
                            }
                        }
                        delete cur;
                    }
                    else if (cur->_right == nullptr) // 右为空
                    {
                        if (cur == _root)
                        {
                            _root = cur->_left;
                        }
                        else
                        {
                            if (parent->_left == cur)
                            {
                                parent->_left = cur->_left;
                            }
                            else
                            {
                                parent->_right = cur->_left;
                            }
                        }

                        delete cur;
                    }
                    else
                    {
                        // 左右都不为空
                        // 右子树最左节点
                        Node *replaceParent = cur;
                        Node *replace = cur->_right;
                        while (replace->_left)
                        {
                            replaceParent = replace;
                            replace = replace->_left;
                        }

                        cur->_key = replace->_key;

                        if (replaceParent->_left == replace)
                            replaceParent->_left = replace->_right;
                        else
                            replaceParent->_right = replace->_right;

                        delete replace;
                    }

                    return true;
                }
            }

            return false;
        }

    private:
        void _Inorder(Node *root)
        {
            if (root == nullptr)
            {
                return;
            }
            _Inorder(root->_left);
            cout << root->_key << ' ';
            _Inorder(root->_right);
        }

    private:
        Node *_root = nullptr;
    };

}


在这里插入图片描述

评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mike!

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值