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

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

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),遍历右子树。

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

相关文章
|
存储 SQL C++
对比 SQL Server中的VARCHAR(max) 与VARCHAR(n) 数据类型
【7月更文挑战7天】SQL Server 中的 VARCHAR(max) vs VARCHAR(n): - VARCHAR(n) 存储最多 n 个字符(1-8000),适合短文本。 - VARCHAR(max) 可存储约 21 亿个字符,适合大量文本。 - VARCHAR(n) 在处理小数据时性能更好,空间固定。 - VARCHAR(max) 对于大文本更合适,但可能影响性能。 - 选择取决于数据长度预期和业务需求。
1128 1
【数据结构OJ题】合并两个有序链表
力扣题目——合并两个有序链表
132 8
【数据结构OJ题】合并两个有序链表
|
SQL 关系型数据库 MySQL
如何查看本地公网 IP 地址?
如何找到本地的公网IP?这篇文章帮到你。
912 3
|
算法 数据可视化 前端开发
第三代软件开发-全新波形抓取算法
欢迎来到我们的 QML & C++ 项目!这个项目结合了 QML(Qt Meta-Object Language)和 C++ 的强大功能,旨在开发出色的用户界面和高性能的后端逻辑。 在项目中,我们利用 QML 的声明式语法和可视化设计能力创建出现代化的用户界面。通过直观的编码和可重用的组件,我们能够迅速开发出丰富多样的界面效果和动画效果。同时,我们利用 QML 强大的集成能力,轻松将 C++ 的底层逻辑和数据模型集成到前端界面中。 在后端方面,我们使用 C++ 编写高性能的算法、数据处理和计算逻辑。C++ 是一种强大的编程语言,能够提供卓越的性能和可扩展性。我们的团队致力于优化代码,减少资
|
运维 Prometheus Cloud Native
GitHub强势置顶!阿里资深老专家微服务容器实战开发笔记限时开源
今天给大家带来的是:尹为强老师著的 《微服务容器化开发实战》,基于SpringCloud、Docker、Rancher、Prometheus和Kubernetes,从设计、开发、部署到运维的云原生整体解决方案
Burpsuite系列 -- (PC端、手机端)抓包配置
Burpsuite系列 -- (PC端、手机端)抓包配置
350 0
Burpsuite系列 -- (PC端、手机端)抓包配置
|
前端开发
CSS基础教程7——盒子模型
盒子由:内容(content),内边距区域(padding),边框(border),外边距(margin)组成一个基本的模型。
CSS基础教程7——盒子模型
|
关系型数据库 开发工具 PostgreSQL
|
缓存 监控 JavaScript
VS Code 是如何优化启动性能的?
本文主要是对 CovalenceConf 2019: Visual Studio Code – The First Second 这次分享的介绍,CovalenceConf 是一个以 Electron 构建桌面软件为主题的技术会议,这也是 VS Code 团队为数不多的对外分享之一(质量较高),主要分享了 VS Code 是如何优化启动性能的。
11774 4
VS Code 是如何优化启动性能的?
|
弹性计算 Linux 数据安全/隐私保护
学习
学习