0
点赞
收藏
分享

微信扫一扫

C++:二叉搜索树

蓝哆啦呀 2024-07-24 阅读 30
c++

概念

二叉搜索树(BST - Binary Search Tree)是一种特殊的二叉树,每个顶点最多可以有两个子节点。其遵顼以下规则:

其有两个特性:

  1. 查找数据非常快,每次查找数据,只需要将数据与当前节点比较,然后决定去左子树还是右子树找,如果最后找到了nullptr,那就是不存在。
  2. 二叉搜索树的中序遍历,得到值是有序的。正是因为二叉搜索树左子树的值都小于根,右子树的值都大于根。中序遍历是 左子树 - 根 - 右子树,所以最后得到的数据就是有序的

 


模拟实现

节点类

template<class K>
struct BSTreeNode
{
    typedef BSTreeNode<K> Node;

    Node* _left;
    Node* _right;
    K _key;
    
    BSTreeNode(const K& key)
        :_left(nullptr)
        ,_right(nullptr)
        ,_key(key)
    {}
};

二叉搜索树类

template<class K>
class BSTree
{
    typedef BSTreeNode<K> Node;
private:
    Node* _root = nullptr;
};

 

插入

想要对二叉搜索树进行节点插入,有两种情况:

值得注意的是:如果某一个值不存在于二叉搜索树中,那么插入这个值一定是在nullptr插入

bool Insert(const K& key)
{
    if (_root == nullptr)
    {
        _root = new Node(key);
        return true;
    }
    
    Node* cur = _root;
    Node* parent = nullptr;

    while (cur)
    {
        if (key < cur->_key)
        {
            parent = cur;
            cur = cur->_left;
        }
        else if (key > cur->_key)
        {
            parent = cur;
            cur = cur->_right;
        }
        else
        {
            return false;
        }
    }

    cur = new Node(key);
    if (key < parent->_key)
        parent->_left = cur;
    else
        parent->_right = cur;

    return true;
}
中序遍历

中序遍历,对于二叉树而言是一个比较简单的操作,我们看到以下代码:

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

    InOrder(root->_left);
    cout << root->_key << " - ";
    InOrder(root->_right);
}

以上代码,先遍历左子树InOrder(root->_left);,然后输出当前节点的值cout << root->_key << " - ";,再遍历右子树InOrder(root->_right);。是一个很常规的中序遍历,但是存在一个问题:这个函数外部没法传参。 

 比如说我要遍历某一个二叉搜索树bst

bst.InOrder(bst._root);

这个调用是存在问题的,那就是_root是私有成员,外部不能把_root作为参数传入中序遍历的接口。此时我们就需要再在外面套一层接口,像这样:

void InOrder()
{
    _InOrder(_root);
}

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

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

我们给函数InOrder创建了一个子函数_InOrder,外部无需传参就可以调用InOrder。我们将中序遍历的代码写在了子函数中,而在InOrder内部,再去调用这个子函数 _InOrder(_root);,就可以正常传参了。 

 

查找

想要查找,基本逻辑就是:

bool Find(const K& key)
{
    Node* cur = _root;

    while (cur)
    {
        if (key < cur->_key)
            cur = cur->_left;
        else if (key > cur->_key)
            cur = cur->_right;
        else
            return true;
    }

    return false;
}

 

删除

首先,既然要删除特定的节点,那么我们就要先查找到该节点。既然要修改该节点,那么也要查找到其父节点,这个思路与前面的插入接口非常相似。      

只要被删除的节点,有一个子树为nullptr,那么就可以将另外一个子树直接链接到其父节点下。

这里还要额外考虑根节点的情况

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

    delete cur;
}
else
{
	//其他情况
}

当待删除节点的左右子树都不为空 

有两种解决方案:

取出节点后,我们把右子树最小节点rightMin的值交给待删除节点的cur,但是rightMin还有可能有右子树,那么就要把右子树移交给rightMin的父节点。

Node* rightMinParent = cur;
Node* rightMin = cur->_right;

while (rightMin->_left)
{
    rightMinParent = rightMin;
    rightMin = rightMin->_left;
}

cur->_key = rightMin->_key;

if (rightMinParent->_left == rightMin)
    rightMinParent->_left == rightMin->_right;
else
    rightMinParent->_right == rightMin->_right;

delete rightMin;

删除总代码:

bool Erase(const K& key)
{
    Node* cur = _root;
    Node* parent = nullptr;

    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* rightMinParent = cur;
                Node* rightMin = cur->_right;
                while (rightMin->_left)
                {
                    rightMinParent = rightMin;
                    rightMin = rightMin->_left;
                }

                cur->_key = rightMin->_key;
                if (rightMin == rightMinParent->_left)
                    rightMinParent->_left = rightMin->_right;
                else
                    rightMinParent->_right = rightMin->_right;

                delete rightMin;
            }

            return true;
        }
    }

    return false;
}

 

析构函数

想要删除整棵树,那就需要递归式地删除每一个节点,为了保证树的节点不会错乱,我们最好通过后序遍历删除,代码如下:

~BSTree()
{
    Destroy(_root);
}

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

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

    delete root;
}
拷贝构造

想要拷贝一棵树出来,我们也需要进行递归式的深拷贝,不过由于要先有父节点,再有子节点,所以要用前序遍历。代码如下:

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

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;
}
赋值重载

代码如下:

BSTree<K>& operator=(BSTree<K> t)
{
    swap(_root, t._root);
    return *this;
}

 

递归查找
bool FindR(const K& key)
{
    return _FindR(_root, key);
}

bool _FindR(Node* root, const K& key)
{
    if (root == nullptr)
        return false;

    if (key < root->_key)
        return _FindR(root->_left, key);
    else if (key > root->_key)
        return _FindR(root->_right, key);
    else
        return true;
}

递归插入
bool InsertR(const K& key)
{
    return _InsertR(_root, key);
}

bool _InsertR(Node*& root, const K& key)
{
    if (root == nullptr)
    {
        root = new Node(key);
        return true;
    }

    if (key < root->_key)
        return _InsertR(root->_left, key);
    else if (key > root->_key)
        return _InsertR(root->_right, key);
    else
        return false;
}

递归删除
bool EraseR(const K& key)
{
    return _EraseR(_root, key);
}

bool _EraseR(Node*& root, const K& key)
{
    if (root == nullptr)
        return false;

    if (key < root->_key)
    {
        return _EraseR(root->_left, key);
    }
    else if (key > root->_key)
    {
        return _EraseR(root->_right, key);
    }
    else
    {
    	//删除节点
    }
}

通过递归找到删除节点后,要考虑两种情况:

先看到第一种:

Node* del = root;

if (root->_left == nullptr)
{
	root = root->_right;
}
else if (root->_right == nullptr)
{
	root = root->_left;
}
else
{
}
delete del;
return true;

 接下来我们再看到左右子树都不为nullptr的情况:

 else
{
    Node* rightMin = root->_right;

    while (rightMin->_left)
    {
        rightMin = rightMin->_left;
    }

    swap(root->_key, rightMin->_key);

    return _EraseR(root->_right, key);
}

总代码展示

BinarySearchTree.h

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

template<class K>
struct BSTreeNode
{
    typedef BSTreeNode<K> Node;

    Node* _root;
    Node* _left;
    Node* _right;
    K _key;

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

template<class K>
class BSTree
{
    typedef BSTreeNode<K> Node;
public:
    BSTree() = default;

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

    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;
    }
        
    ~BSTree()
    {
        Destroy(_root);
    }

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

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

        delete root;
    }

    BSTree<K>& operator=(BSTree<K> t)
    {
        swap(_root, t._root);
        return *this;
    }

    bool Insert(const K& key)
    {
        if (_root == nullptr)
        {
            _root = new Node(key);
            return true;
        }
        
        Node* cur = _root;
        Node* parent = nullptr;

        while (cur)
        {
            if (key < cur->_key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (key > cur->_key)
            {
                parent = cur;
                cur = cur->_right;
            }
            else
            {
                return false;
            }
        }

        cur = new Node(key);
        if (key < parent->_key)
            parent->_left = cur;
        else
            parent->_right = cur;

        return true;
    }

    bool Erase(const K& key)
    {
        Node* cur = _root;
        Node* parent = nullptr;

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

                    delete cur;
                }
                else
                {
                    Node* rightMinParent = cur;
                    Node* rightMin = cur->_right;

                    while (rightMin->_left)
                    {
                        rightMinParent = rightMin;
                        rightMin = rightMin->_left;
                    }

                    cur->_key = rightMin->_key;

                    if (rightMinParent->_left == rightMin)
                        rightMinParent->_left == rightMin->_right;
                    else
                        rightMinParent->_right == rightMin->_right;

                    delete rightMin;
                }

                return true;
            }
        }

        return false;
    }

    bool Find(const K& key)
    {
        Node* cur = _root;

        while (cur)
        {
            if (key < cur->_key)
                cur = cur->_left;
            else if (key > cur->_key)
                cur = cur->_right;
            else
                return true;
        }

        return false;
    }

    void InOrder()
    {
        _InOrder(_root);
        cout << "end" << endl;
    }

    bool FindR(const K& key)
    {
        return _FindR(_root, key);
    }

    bool InsertR(const K& key)
    {
        return _InsertR(_root, key);
    }

    bool EraseR(const K& key)
    {
        return _EraseR(_root, key);
    }

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

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

    bool _FindR(Node* root, const K& key)
    {
        if (root == nullptr)
            return false;

        if (key < root->_key)
            return _FindR(root->_left, key);
        else if (key > root->_key)
            return _FindR(root->_right, key);
        else
            return true;
    }

    bool _InsertR(Node*& root, const K& key)
    {
        if (root == nullptr)
        {
            root = new Node(key);
            return true;
        }

        if (key < root->_key)
            return _InsertR(root->_left, key);
        else if (key > root->_key)
            return _InsertR(root->_right, key);
        else
            return false;
    }

    bool _EraseR(Node*& root, const K& key)
    {
        if (root == nullptr)
            return false;

        if (key < root->_key)
        {
            return _EraseR(root->_left, key);
        }

        else if (key > root->_key)
        {
            return _EraseR(root->_right, key);
        }
        else
        {
            Node* del = root;

            if (root->_left == nullptr)
            {
                root = root->_right;
            }
            else if (root->_right == nullptr)
            {
                root = root->_left;
            }
            else
            {
                Node* rightMin = root->_right;

                while (rightMin->_left)
                {
                    rightMin = rightMin->_left;
                }

                swap(root->_key, rightMin->_key);

                return _EraseR(root->_right, key);
            }
            delete del;
            return true;
        }
    }

    Node* _root = nullptr;
};
举报

相关推荐

0 条评论