一篇文章教会你什么是二叉搜索树(中)

简介: 二叉搜索树插入迭代写法

5.二叉搜索树插入

迭代写法

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;
  }
  1. 首先,函数检查树是否为空(即 _root 是否为 nullptr)。如果树为空,它会创建一个新的根节点,该节点的关键字为 key,然后返回 true 表示插入成功。
  2. 如果树不为空,函数会进入一个循环,用来搜索插入位置。循环中的代码执行以下操作:
  • 如果当前节点的关键字小于 key,则继续向右子树移动,将 parent 设置为当前节点,以便稍后插入新节点到右子树。
  • 如果当前节点的关键字大于 key,则继续向左子树移动,同样将 parent 设置为当前节点,以便稍后插入新节点到左子树。
  • 如果当前节点的关键字等于 key,则返回 false,表示不允许重复关键字的节点插入。
  1. 一旦找到插入位置,函数创建一个新的节点 cur,该节点的关键字为 key
  2. 接着,根据 parent 节点的关键字与 key 的比较结果,将新节点 cur 插入到正确的位置。如果 parent->_key < key,则将 cur 插入为 parent 的右子节点,否则插入为 parent 的左子节点。
  3. 最后,函数返回 true,表示插入成功。

这个 Insert 函数实现了BST的插入操作,它确保新节点按照BST的有序性质被插入到正确的位置。如果关键字已经存在于树中,函数会返回 false,表示插入失败(因为BST不允许重复关键字)。

递归写法

bool InsertR(const K& key)
{
    return _InsertR(_root, key);
} 
private:
  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;
    }
  1. bool InsertR(const K& key)
  • 这是公有函数,用于在二叉搜索树中插入新节点。它首先调用私有递归函数 _InsertR,并将根节点 _root 和要插入的关键字 key 作为参数传递给它。
  1. bool _InsertR(Node*& root, const K& key)
  • 这是私有递归辅助函数,用于在给定的二叉搜索树中插入新节点。
  • 函数首先检查当前根节点 root 是否为空。如果为空,表示找到了插入位置,它会创建一个新节点并将其关键字设置为 key,然后将 root 指向新节点,最后返回 true 表示插入成功。
  • 如果当前根节点root不为空,它会比较当前节点的关键字root->_key与要插入的关键字key
  • 如果 root->_key < key,则递归地在右子树中插入新节点,调用 _InsertR(root->_right, key)
  • 如果 root->_key > key,则递归地在左子树中插入新节点,调用 _InsertR(root->_left, key)
  • 如果 root->_key == key,表示已经存在相同关键字的节点,直接返回 false 表示插入失败。

6.二叉搜索树查找

迭代写法

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 Find(const K& key)

  • 这是一个公有函数,用于查找二叉搜索树中是否存在指定关键字 key
  • 函数首先初始化一个指针 cur 为根节点 _root,然后进入一个循环。
  • 在循环中,它会不断根据当前节点的关键字与目标关键字key的比较结果来决定向左子树还是右子树移动:
  • 如果当前节点的关键字小于 key,则说明要查找的关键字应该在当前节点的右子树中,所以将 cur 指向右子节点 cur->_right
  • 如果当前节点的关键字大于 key,则说明要查找的关键字应该在当前节点的左子树中,所以将 cur 指向左子节点 cur->_left
  • 如果当前节点的关键字等于 key,则表示找到了目标关键字,函数返回 true 表示查找成功。
  • 循环会一直进行,直到 cur 指向空(nullptr),这意味着整棵树都被搜索过了,但没有找到目标关键字,所以函数返回 false 表示查找失败。

递归写法

bool FindR(const K& key)
{
    return _FindR(_root, key);
}
private:
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;
}
  1. bool FindR(const K& key)
  • 这是公有函数,用于在二叉搜索树中查找指定关键字 key 是否存在。它首先调用私有的递归函数 _FindR,并将根节点 _root 和要查找的关键字 key 作为参数传递给它。
  1. bool _FindR(Node* root, const K& key)
  • 这是私有的递归辅助函数,用于在给定的二叉搜索树中递归查找指定关键字 key 是否存在。
  • 函数首先检查当前根节点 root 是否为空(nullptr)。如果为空,表示已经搜索到了树的底部,关键字 key 不存在于树中,所以返回 false 表示查找失败。
  • 如果当前根节点root不为空,函数会比较当前节点的关键字root->_key与要查找的关键字key
  • 如果 root->_key < key,则说明要查找的关键字应该在当前节点的右子树中,所以递归调用 _FindR(root->_right, key) 在右子树中查找。
  • 如果 root->_key > key,则说明要查找的关键字应该在当前节点的左子树中,所以递归调用 _FindR(root->_left, key) 在左子树中查找。
  • 如果 root->_key == key,表示找到了目标关键字,函数返回 true 表示查找成功。

这个递归查找操作会在树的节点之间不断递归地进行,直到找到目标关键字或确认目标不存在。如果目标关键字存在于树中,函数返回 true;否则,返回 false

7.二叉搜索树删除

迭代写法

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
        {
            // 1、左为空
            // 2、右为空
            // 3、左右都不为空
            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;
                cur = nullptr;
            }
            else if (cur->_right == nullptr)
            {
                if (_root == cur)
                {
                    _root = cur->_left;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        parent->_left = cur->_left;
                    }
                    else
                    {
                        parent->_right = cur->_left;
                    }
                }
                delete cur;
                cur = nullptr;
            }
            else
            {
                // 找到右子树最小节点进行替换
                Node* minParent = cur;
                Node* min = cur->_right;
                while (min->_left)
                {
                    minParent = min;
                    min = min->_left;
                }
                swap(cur->_key, min->_key);
                if (minParent->_left == min)
                    minParent->_left = min->_right;
                else
                    minParent->_right = min->_right;
                delete min;
            }
            return true;
        }
    }
    return false;
}
  1. 首先,函数初始化两个指针 parentcur 分别为 nullptr 和根节点 _root,然后进入一个循环。这个循环用于在BST中找到待删除节点,并进行删除操作。
  2. 在循环中,函数会不断根据当前节点的关键字与目标关键字 key 的比较结果来决定向左子树还是右子树移动,同时更新 parentcur 指针。
  3. 如果找到了目标关键字key,表示要开始删除操作。删除操作分为以下几种情况:
  • 情况1:当前节点的左子树为空。在这种情况下,直接用当前节点的右子树替换当前节点即可。
  • 情况2:当前节点的右子树为空。在这种情况下,直接用当前节点的左子树替换当前节点即可。
  • 情况3:当前节点的左右子树都不为空。在这种情况下,需要找到右子树中的最小节点(通常是右子树中最左边的节点),将其关键字与当前节点的关键字进行交换,然后删除最小节点。
  1. 删除操作执行完毕后,函数返回 true 表示删除成功。
  2. 如果循环结束时仍然没有找到目标关键字 key,说明关键字不存在于树中,函数返回 false 表示删除失败。

递归写法

bool EraseR(const K& key)
{
    return _EraseR(_root, key);
}
private:
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
    {
        Node* del = root;
        if (root->_left == nullptr)
        {
            root = root->_right;
        }
        else if (root->_right == nullptr)
        {
            root = root->_left;
        }
        else
        {
            // 找右树的最左节点替换删除
            Node* min = root->_right;
            while (min->_left)
            {
                min = min->_left;
            }
            swap(root->_key, min->_key);
            return _EraseR(root->_right, key);
        }
        delete del;
        return true;
    }
}
  1. bool EraseR(const K& key)
  • 这是公有函数,用于在二叉搜索树中删除指定关键字 key 的节点。它首先调用私有的递归函数 _EraseR,并将根节点 _root 和要删除的关键字 key 作为参数传递给它。
  1. bool _EraseR(Node*& root, const K& key)
  • 这是私有的递归辅助函数,用于在给定的二叉搜索树中递归删除指定关键字 key 的节点。
  • 函数首先检查当前根节点 root 是否为空(nullptr)。如果为空,表示已经搜索到了树的底部,关键字 key 不存在于树中,所以返回 false 表示删除失败。
  • 如果当前根节点root不为空,函数会比较当前节点的关键字root->_key与要删除的关键字key
  • 如果 root->_key < key,则说明要删除的关键字应该在当前节点的右子树中,所以递归调用 _EraseR(root->_right, key) 在右子树中进行删除操作。
  • 如果 root->_key > key,则说明要删除的关键字应该在当前节点的左子树中,所以递归调用 _EraseR(root->_left, key) 在左子树中进行删除操作。
  • 如果 root->_key == key,表示找到了要删除的目标节点,此时需要执行删除操作。
  1. 删除操作执行的逻辑分为以下几种情况:
  • 情况1:当前节点的左子树为空。在这种情况下,可以直接用当前节点的右子树替换当前节点。
  • 情况2:当前节点的右子树为空。在这种情况下,可以直接用当前节点的左子树替换当前节点。
  • 情况3:当前节点的左右子树都不为空。在这种情况下,需要找到右子树中的最小节点,通常是右子树中最左边的节点,将其关键字与当前节点的关键字进行交换,然后继续递归删除右子树中的最小节点。
  1. 删除操作执行完毕后,函数返回 true 表示删除成功。
  2. 如果循环结束时仍然没有找到目标关键字 key,说明关键字不存在于树中,函数返回 false 表示删除失败。

8.二叉搜索树中序遍历

void InOrder()
{
    _InOrder(_root);
    cout << endl;
}
private:
  void _InOrder(Node* root)
    {
        if (root == nullptr)
        {
            return;
        }
        _InOrder(root->_left);
        cout << root->_key << " ";
        _InOrder(root->_right);
    }
  1. void InOrder()
  • 这是公有函数,用于执行中序遍历二叉搜索树的操作。
  • 首先,它调用了私有的递归函数 _InOrder(_root),并传递根节点 _root 作为参数,开始进行中序遍历。
  • 遍历完成后,会输出一个换行符,以便在输出时每个节点值都在一行上。
  1. void _InOrder(Node* root)
  • 这是私有的递归辅助函数,用于执行中序遍历的实际操作。
  • 函数首先检查当前根节点 root 是否为空。如果为空,表示到达树的底部,直接返回,不执行任何操作。
  • 如果当前节点不为空,函数按照中序遍历的顺序执行以下操作:
  • 递归调用 _InOrder(root->_left),遍历左子树。
  • 输出当前节点的关键字值 root->_key,通常是将节点值打印到控制台。
  • 递归调用 _InOrder(root->_right),遍历右子树。

这个中序遍历函数会遍历整个二叉搜索树,并按照从小到大的顺序输出节点的关键字值。这是一种常用的方式来访问二叉搜索树的所有节点,并按照升序排列输出它们的值。

相关文章
【五一专栏】1. 迭代版二叉树的前、中、后序遍历
【五一专栏】1. 迭代版二叉树的前、中、后序遍历
|
存储 算法 编译器
一篇文章教会你什么是二叉搜索树(上)
二叉搜索树概念 二叉搜索树(Binary Search Tree,BST)是一种二叉树的特殊形式,它具有以下关键性质:
|
7月前
【一刷《剑指Offer》】面试题 18:树的子结构
【一刷《剑指Offer》】面试题 18:树的子结构
|
7月前
|
算法 Java
数据结构奇妙旅程之二叉树题型解法总结
数据结构奇妙旅程之二叉树题型解法总结
|
存储 算法 索引
数据结构与算法之十 提高二叉搜索树的效率
数据结构与算法之十 提高二叉搜索树的效率
97 0
一文教会你单向链表 1
一文教会你单向链表
|
算法 程序员
程序员怎能不会二叉树系列(一)简单了解二叉树
程序员怎能不会二叉树系列(一)简单了解二叉树
代码随想录刷题|二叉树的理论基础、 二叉树的遍历 LeetCode 144、145、94、120(下)
代码随想录刷题|二叉树的理论基础、 二叉树的遍历 LeetCode 144、145、94、120
代码随想录刷题|二叉树的理论基础、 二叉树的遍历 LeetCode 144、145、94、120(下)