详解二叉搜索树

一、基本概念

二叉搜索树(BST)又称二叉排序树,它是一颗空树或者具有以下性质的二叉树:

  1. 若左树不为空,左树上所有节点的值都小于等于根节点的值
  2. 若右树不为空,右树上所有节点的值都大于等于根节点的值
  3. 左右子树也为二叉搜索树
    在这里插入图片描述

二、二叉搜索树特性

2.1 顺序性

当对二叉搜索树进行中序遍历时,遍历的结构是有序的。

2.2 局限性

二分搜索树在时间性能上是具有局限性的。

在理想情况下,二叉搜索树是“平衡”的,这样就可以在 log⁡𝑛 轮循环内查找任意节点。

然而,如果我们在二叉搜索树中不断地插入和删除节点,可能导致二叉树退化为链表,相应的,二叉搜索树的查找操作是和这棵树的高度相关的,而此时这颗树的高度就是这颗树的节点数 n,这时各种操作的时间复杂度也会退化为 𝑂(𝑛) 。

在这里插入图片描述

三、二叉搜索树实现

3.1 插入

插入过程如下:

  1. 当树为空时,该结点就是root结点
  2. 树不为空时,大于根结点往右走,小于根结点往左走,直到找到空位置,插入新结点
  3. 如果要插入相同的值,则进行定义,如:相同的统一往右/左走,直到找到空位置,插入新结点

在这里插入图片描述

在这棵树上分别插入2和16这两个结点,会得到如下的树:
在这里插入图片描述

代码实现如下:

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

3.2 删除

要删除一个结点,首先要保证该结点存在,当查找到结点后,会有以下四种情况:

  1. 该结点左右孩子均为空
  2. 该结点左孩子为空,右孩子不为空
  3. 该结点右孩子为空,左孩子不为空
  4. 该结点左右孩子均不为空

为了应对这四种情况,有以下四种解决方案:

  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
                {
                    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;
}

3.3 查找

查找相对来说很简单,大于该结点往左走,小于该结点往右走,如此进行查找即可

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

四、二叉搜索树key和key/value使用

只有key值的二叉搜索树只支持查找功能,比如:我们要查找某个单词是否在字典中存在,停车场是否存在某辆车等等,但对于其它信息我们无法获取如:单词的意思,车辆停车时长。这样的功能有点单一,而且不支持修改,因为一修改就会破坏其内部结构。所以就有了key/value这种结构,对于每个键值可以赋予一个value,value类型可以是任意对象,这样便可满足对二叉搜索树的修改,因为:key是确定的,我们修改是修改的是对应的value值

这样二叉搜索树便可满足如查询单词,查询车辆的停车时长,单词的引用次数一些简单需求。

对于key/value二叉搜索树的实现与key二叉搜索树类似

五、key/value二叉搜索树

template<class K, class V>
struct BSTNode
{
        K _key;
        V _value;

        BSTNode<K, V>* _left;
        BSTNode<K, V>* _right;

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

// Binary Search Tree
// Key/value
template<class K, class V>
class BSTree
{
        typedef BSTNode<K> Node;
public:
        // 强制生成构造
        BSTree() = default;

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

        BSTree& operator=(BSTree tmp)
        {
            swap(_root, tmp._root);
            return *this;
        }

        ~BSTree()
        {
            Destroy(_root);
            _root = nullptr;
        }

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

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

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

        void InOrder()
        {
            _InOrder(_root);
            cout << endl;
        }
private:
        void _InOrder(Node* root)
        {
            if (root == nullptr)
            {
                    return;
            }

            _InOrder(root->_left);
            cout << root->_key << ":" << root->_value << endl;
            _InOrder(root->_right);
        }

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

            Destroy(root->_left);
            Destroy(root->_right);
            delete root;
        }
private:
        Node* _root = nullptr;
};

完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值