C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、二叉搜索树简介

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

C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP


二、详细操作

C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP

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

1.查找

       a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

       b、最多查找高度次,走到到空,还没找到,这个值不存在。

2.插入

插入的具体过程如下

       a. 树为空,则直接新增节点,赋值给root指针

       b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP

3. 删除

       首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

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

C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP


 三、具体实现

#include<iostream>
using namespace std;

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

    BSTreeNode(const K& key)
        :_left(nullptr)
        ,_right(nullptr)
        ,_key(key)
    {}
};
template<class K>
class BSTree
{
    typedef BSTreeNode<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)
        {
            parent = cur;
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
        cur = new Node(key);
        if (parent->_key < key)
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;

        }
        return true;
    }
    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 (cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else 
                        {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                else if (cur->_right == nullptr)
                {
                    if (cur == _root)
                    {
                        _root = cur -> _left;
                    }
                    else
                    {
                        if (cur == parent->_left)
                        {
                            parent->_left = cur->_left;
                        }
                        else
                        {
                            parent->_right = cur->_left;
                        }
                    }
                    delete cur;
                }
                else
                {
                    Node* parent = cur;
                    Node* subleft = cur->_right;
                    while (subleft->_left)
                    {
                        parent = subleft;
                        subleft = subleft->_left;
                
                    }
                    swap(cur->_key, subleft->_key);

                    if (subleft == parent->_left)
                    {
                        parent->_left = subleft->_right;
                    }
                    else
                    {
                        parent->_right = subleft->_right;
                    }
                    delete subleft;
                }
                return true;
            }
        }
        return false;
    }
    ~BSTree()
    {
        Destory(_root);
    }

    bool EraseR(const K& key)
    {
        return _EraseR(_root, key);
    }
    bool InsertR(const K& key)
    {
        return _InsertR(_root,key);
    }
    bool findR(const K& key)
    {
        return _findR(_root, key);
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    BSTree(const BSTree<K>& t)
    {
        _root = Copy(t._root);
    }
    //C++11
    BSTree()
    {}
    
    BSTree<K>& operator=(BSTree<K> t)
    {
        swap(_root,t._root);
        return *this;
    }
private:
    Node* _root = nullptr;
    Node* Copy(Node* root)
    {
        if (root == nullptr)
            return nullptr;

        Node* newRoot = new Node(root->_key);
        newRoot->_left = Copy(root->_left);
        newRoot->_right = Copy(root->_right);

        return newRoot;

    }
    void Destory(Node*& root)
    {

        if (root == nullptr)
            return;
        Destory(root->_left);
        Destory(root->_right);
        delete root;
        root = nullptr;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;
        _InOrder(root->_left);
        cout << root->_key << " ";
        _InOrder(root->_right);
    }
    bool _InsertR(Node*& root,const K& key)
    {
        if (root == nullptr)
        {
            root = new Node(key);
            return true;
        }
        if (root->_key < key)
        {
            return  _InsertR(root->_right,key);
        }
        else if (root->_key > key)
        {
            return  _InsertR(root->_left,key);
        }
        else
        {
            return false;
        }
    }
    bool _findR(Node* root, const K* key)
    {
        if (root == nullptr)
            return false;
        if (root->_key < key)
        {
            return _findR(root->_right, key);
        }
        else if (root->_key > key)
        {
            return _findR(root->_left, key);
        }
        else
        {
            return true;
        }
    }
    bool _EraseR(Node*& root, const K& key)
    {
        if (root == nullptr)
            return false;
        if (root->_key < key)
        {
            return _EraseR(root->_right, key);
        }
        else if (root->_key > key)
        {
            return _EraseR(root->_left, key);
        }
        else
        {
            if (root->_left == nullptr)
            {
                Node* del = root;
                root = root->_right;
                delete del;
            }
            else if (root->_right == nullptr)
            {
                Node* del = root;
                root = root->_left;
                delete del;
            }
            else
            {
                Node* subleft = root->_right;
                while (subleft->_left)
                {
                    subleft = subleft->_left;
                }
                swap(subleft->_key, root->_key);

                return _EraseR(root->_right, key);
            }
        }
        return false;
    }  
};
int main()
{
    int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    BSTree<int> bt;
    for (auto e : a)
    {
        bt.InsertR(e);
    }
    bt.InOrder();

    bt.EraseR(14);
    bt.InOrder();

    bt.EraseR(3);
    bt.InOrder();

    bt.EraseR(8);
    bt.InOrder();

    for (auto e : a)
    {
        bt.EraseR(e);
        bt.InOrder();
    }
    return 0;
}

四、具体应用

1.K模型

       K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

2.KV模型

       每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:

//改造二叉搜索树为KV结构
template<class K,class V>
struct BSTreeNode
{
    BSTreeNode<K,V>* _left;
    BSTreeNode<K,V>* _right;
    K _key;
    V _value;

    BSTreeNode(const K& key,const V& value)
        :_left(nullptr)
        , _right(nullptr)
        , _key(key)
        ,_value(value)
    {}
};
template<class K,class V>
class BSTree
{
    typedef BSTreeNode<K,V> Node;
public:
    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)
        {
            parent = cur;
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                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 (cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else
                        {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                else if (cur->_right == nullptr)
                {
                    if (cur == _root)
                    {
                        _root = cur->_left;
                    }
                    else
                    {
                        if (cur == parent->_left)
                        {
                            parent->_left = cur->_left;
                        }
                        else
                        {
                            parent->_right = cur->_left;
                        }
                    }
                    delete cur;
                }
                else
                {
                    Node* parent = cur;
                    Node* subleft = cur->_right;
                    while (subleft->_left)
                    {
                        parent = subleft;
                        subleft = subleft->_left;

                    }
                    swap(cur->_key, subleft->_key);

                    if (subleft == parent->_left)
                    {
                        parent->_left = subleft->_right;
                    }
                    else
                    {
                        parent->_right = subleft->_right;
                    }
                    delete subleft;
                }
                return true;
            }
        }
        return false;
    }
    ~BSTree()
    {
        Destory(_root);
    }
    void InOrder()
    {
        _InOrder(_root);        
    }
private:
    Node* _root = nullptr;    
    void Destory(Node*& root)
    {
        if (root == nullptr)
            return;
        Destory(root->_left);
        Destory(root->_right);
        delete root;
        root = nullptr;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;
        _InOrder(root->_left);
        cout << root->_key << ":" << root->_value << endl;
        _InOrder(root->_right);
    }
};

五、性能分析

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

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

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

C++进阶之路---二叉搜索树详解 | 具体实现-LMLPHP

       最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(logN)

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

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


结语:C++关于二叉搜索树的分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

03-12 22:14