搜索二叉树--C++思路及实现

一、二叉搜索树节点类(BSTreeNode)

template<class K, class V>
class BSTreeNode
{
public:
    BSTreeNode(K key = K(), V value = V())
        :_key(key),
        _value(value),
        _left(nullptr),
        _right(nullptr)
    {

    }
    K _key;
    V _value;
    BSTreeNode* _left;
    BSTreeNode* _right;
};

这个类定义了二叉搜索树的节点。它包含了键值对(_key_value)以及指向左右子节点的指针(_left_right)。构

二、二叉搜索树类(BSTree)

插入操作(Insert)

造函数允许用户在创建节点时指定键值对,也可以使用默认值。

template<class K, class V>
class BSTree
{
    //...

public:
    bool Insert(const K& key, const V& value)
    {
        Node* newnode = new Node(key, value);
        if (_root == nullptr)
        {
            _root = newnode;
            return true;
        }
        Node* newnodeP = nullptr;
        Node* cur = _root;
        while (cur!= nullptr)
        {
            if (cur->_key > key)
            {
                newnodeP = cur;
                cur = cur->_left;
            }
            else if (cur->_key < key)
            {
                newnodeP = cur;
                cur = cur->_right;
            }
            else if(cur->_key == key)
            {
                return false;
            }
        }
        if (cur == newnodeP->_left)
            newnodeP->_left = newnode;
        else
            newnodeP->_right = newnode;
        return true;
    }
    //...
};

插入操作首先创建一个新节点。如果树为空,则新节点成为根节点。如果树不为空,从根节点开始遍历,根据键的大小决定向左子树或右子树移动,直到找到合适的插入位置。如果要插入的键已经存在于树中,则返回false,表示插入失败。

图形解释:
假设要插入键值对(5, "value5")到一个已有的二叉搜索树中。如果当前树的结构为:

    3
   / \
  1   4

从根节点 3 开始,5 大于 3,所以移动到右子树 4。由于 4 没有子节点,且 5 大于 4,所以将新节点插入到 4 的右子树位置。插入后的树结构为:

    3
   / \
  1   4
       \
        5

查找操作(Find)

Node* Find(const K& key)
{
    Node* cur = _root;
    while (cur!= nullptr)
    {
        if (cur->_key > key)
            cur = cur->_left;
        else if (cur->_key < key)
            cur = cur->_right;
        else
            return cur;
    }
    return nullptr;
}

查找操作从根节点开始,根据要查找的键与当前节点键的大小关系,向左子树或右子树移动,直到找到目标键或者遍历到空节点。如果找到目标键,则返回对应的节点指针;如果未找到,则返回nullptr

图形解释:
比如要在上面的树中查找键为 4 的节点。从根节点 3 开始,4 大于 3,移动到右子树 4,找到目标节点并返回其指针。

删除操作(Erase)

bool Erase(const K& key)
{
    Node* cur = _root;
    Node* curP = nullptr;
    while (cur!= nullptr)
    {
        if (cur->_key > key)
        {
            curP = cur;
            cur = cur->_left;
        }
        else if (cur->_key < key)
        {
            curP = cur;
            cur = cur->_right;
        }
        else
            break;
    }
    if (cur == nullptr)
        return false;

    if (cur->_left == nullptr)
    {
        if (cur == _root)
        {
            _root = cur->_right;
        }
        else if (cur == curP->_left)
            curP->_left = cur->_right;
        else
            curP->_right = cur->_right;
        delete cur;
    }
    else if (cur->_right == nullptr)
    {
        if (cur == _root)
        {
            _root = cur->_left;
        }
        else if (cur == curP->_left)
            curP->_left = cur->_left;
        else
            curP->_right = cur->_left;
        delete cur;
    }
    else
    {
        Node* delP = cur;
        Node* exchange = cur->_right;
        while (exchange->_left!= nullptr)
        {
            delP = exchange;
            exchange = exchange->_left;
        }
        cur->_key = exchange->_key;
        cur->_value = exchange->_value;
        if(delP->_left == exchange)
        {
            delP->_left = exchange->_right;
        }
        else
            delP->_right = exchange->_right;
        delete exchange;
    }
    return true;
}

删除操作首先找到要删除的节点。如果节点没有左子树,则将其父节点的相应指针指向该节点的右子树;如果节点没有右子树,则将其父节点的相应指针指向该节点的左子树;如果节点既有左子树又有右子树,则找到该节点右子树中的最小节点,将其值赋给要删除的节点,然后删除最小节点。图形解释:
假设要删除上面树中的节点 4。首先找到节点 4,它有右子树但没有左子树。如果节点 4 是根节点,那么根节点变为 4 的右子树;如果节点 4 是其父节点的左子节点,那么其父节点的左子树变为 4 的右子树;如果节点 4 是其父节点的右子节点,那么其父节点的右子树变为 4 的右子树。删除后的树结构为:

    3
   / \
  1   5

中序遍历(InOrder)

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

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

中序遍历是一种遍历二叉树的方式,它先遍历左子树,然后访问根节点,最后遍历右子树。对于二叉搜索树,中序遍历会得到一个有序的键值对序列。

图形解释:
对于上面的树进行中序遍历,结果为 1:value1 3:value3 5:value5。

测试

void TestBSTree()
{
    string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
    BSTree<string, int> countTree;
    for (auto str : strs)
    {
        auto ret = countTree.Find(str);
        if (ret == NULL)
        {
            countTree.Insert(str, 1);
        }
        else
        {
            ret->_value++;
        }
    }
    countTree.InOrder();
    countTree.Erase("苹果");
    countTree.InOrder();
    countTree.Erase("樱桃");
    countTree.InOrder();
    countTree.Erase("西瓜");
    countTree.InOrder();
}

这个函数首先创建一个字符串和整数的二叉搜索树,用于统计给定字符串数组中每个水果出现的次数。遍历字符串数组,如果某个水果在树中不存在,则插入一个新节点,值为 1;如果水果已经存在,则将对应节点的值加 1。然后通过删除操作依次删除 “苹果”“樱桃”“西瓜”,并在每次删除后进行中序遍历,展示树的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值