二叉查找树 (Binary Search Tree, 简称 BST) 是一种基本的数据结构,其设计核心在于每个节点的值都满足以下性质:

  • 左子树的所有节点值均小于当前节点值。
  • 右子树的所有节点值均大于当前节点值。

这使得二叉查找树能够高效地支持一系列查找相关操作,包括普通查找、前驱后继查询、基于排名的查询以及基于值的排名计算。在树形合理时,bst 可以以 log 的时间快速进行集合查找。

本文将结合代码和理论讲解这些操作,帮助读者全面掌握 BST 的查找机制。

假定我们这样构造二叉树:

struct TreeNode {
int value; // 节点的值
int size; // 以该节点为根的子树大小
TreeNode* left; // 指向左子树
TreeNode* right; // 指向右子树 TreeNode(int v) : value(v), size(1), left(nullptr), right(nullptr) {}
};

1. 普通查找

问题描述

给定一个值 $ x $,我们希望在 BST 中找到值等于 $ x $ 的节点。

查找思路

从根节点开始递归或迭代:

  • 如果当前节点值等于 $ x $,则查找成功。
  • 如果 $ x $ 小于当前节点值,则进入左子树继续查找。
  • 如果 $ x $ 大于当前节点值,则进入右子树继续查找。
  • 如果到达空节点,查找失败。

代码实现

TreeNode* find(TreeNode* root, int value) {
if (!root || root->value == value) return root;
if (value < root->value) return find(root->left, value);
return find(root->right, value);
}

时间复杂度

  • 平衡树:$ O(\log n) $
  • 非平衡树(退化为链表):$ O(n) $

即效率取决于树高。


2. 查找前驱和后继

前驱与后继的定义

  • 前驱:小于给定值的最大节点。
  • 后继:大于给定值的最小节点。

前驱查找

  • 如果节点有左子树,前驱是左子树中值最大的节点。
  • 如果节点没有左子树,沿着父节点回溯,找到第一个右子树包含当前节点的祖先节点。

后继查找

  • 如果节点有右子树,后继是右子树中值最小的节点。
  • 如果节点没有右子树,沿着父节点回溯,找到第一个左子树包含当前节点的祖先节点。

代码实现

TreeNode* findPredecessor(TreeNode* root, int value) {
TreeNode* predecessor = nullptr;
while (root) {
if (value > root->value) {
predecessor = root;
root = root->right;
} else {
root = root->left;
}
}
return predecessor;
} TreeNode* findSuccessor(TreeNode* root, int value) {
TreeNode* successor = nullptr;
while (root) {
if (value < root->value) {
successor = root;
root = root->left;
} else {
root = root->right;
}
}
return successor;
}

3. 以排名查询值

问题描述

给定一个排名 $ k $,找到 BST 中第 $ k $ 小的节点值。

额外信息:子树大小

为了保存排名信息,我们在每个节点存储一个额外的属性 size,表示以当前节点为根的子树大小。

  • 根节点的子树大小为其左子树大小加右子树大小再加 1。
  • 在插入节点时动态更新 size 属性。

查询思路

  1. 计算左子树大小 $ \text{size}_{\text{left}} $。
  2. 如果 $ k = \text{size}_{\text{left}} + 1 $,当前节点即为所求。
  3. 如果 $ k \leq \text{size}_{\text{left}} $,在左子树中递归查找。
  4. 如果 $ k > \text{size}_{\text{left}} + 1 $,在右子树中递归查找,更新 $ k = k - \text{size}_{\text{left}} - 1 $。

代码实现

TreeNode* findByRank(TreeNode* root, int k) {
if (!root) return nullptr;
int leftSize = root->left ? root->left->size : 0;
if (k == leftSize + 1) return root;
if (k <= leftSize) return findByRank(root->left, k);
return findByRank(root->right, k - leftSize - 1);
}

4. 以值查询排名

问题描述

给定一个值 $ x $,求其在 BST 中的排名(即有多少节点值小于 $ x $)。

查询思路

  1. 初始化排名计数器 $ \text{rank} = 0 $。
  2. 遍历树路径:
    • 如果 $ x $ 小于当前节点值,进入左子树。
    • 如果 $ x $ 大于当前节点值,更新 $ \text{rank} += \text{size}_{\text{left}} + 1 $,进入右子树。
    • 如果找到值等于 $ x $,返回当前排名。
  3. 如果到达空节点,说明值不存在。

代码实现

int findRankByValue(TreeNode* root, int value) {
int rank = 0;
while (root) {
if (value < root->value) {
root = root->left;
} else {
rank += (root->left ? root->left->size : 0) + 1;
if (value == root->value) return rank;
root = root->right;
}
}
return -1; // 值不存在
}

总结

操作 方法描述 时间复杂度
查找值 从根节点沿路径递归查找。 O(h)
查找前驱/后继 利用树的结构特性,寻找最接近的节点值。 O(h)
以排名查询值 利用子树大小属性,从根递归寻找第 ( k ) 小节点。 O(h)
以值查询排名 统计路径中左子树大小,计算排名。 O(h)

通过本文的讲解和代码示例,希望读者能够掌握 BST 的查找操作,并能在实际开发中灵活运用这些知识。

然而,需要注意的是,查找操作的效率高度依赖于树的平衡性。当 BST 不平衡时,其性能可能退化到与链表类似,导致时间复杂度变为 $ O(n) $。因此,在进行插入和删除操作时,保持树的平衡是至关重要的。

为了解决这一问题,平衡二叉树(如 AVL 树、红黑树)通过特定的旋转操作,保证了树的高度始终保持在 $ O(\log n) $ 的级别。作为延伸思考,你可以研究平衡树的原理及其在保持效率上的作用。

深入理解二叉查找树(BST)的重要查找操作的更多相关文章

  1. 二叉排序树(BST)创建,删除,查找操作

    binary search tree,中文翻译为二叉搜索树.二叉查找树或者二叉排序树.简称为BST 一:二叉搜索树的定义 他的定义与树的定义是类似的,也是一个递归的定义: 1.要么是一棵空树 2.如果 ...

  2. 查找系列合集-二叉查找树BST

    一. 二叉树 1. 什么是二叉树? 在计算机科学中,二叉树是每个结点最多有两个子树的树结构. 通常子树被称作“左子树”(left subtree)和“右子树”(right subtree). 二叉树常 ...

  3. [学习笔记] 二叉查找树/BST

    平衡树前传之BST 二叉查找树(\(BST\)),是一个类似于堆的数据结构, 并且,它也是平衡树的基础. 因此,让我们来了解一下二叉查找树吧. (其实本篇是作为放在平衡树前的前置知识的,但为了避免重复 ...

  4. 3.2 符号表之二叉查找树BST

    一.插入和查找 1.二叉查找树(Binary Search Tree)是一棵二叉树,并且每个结点都含有一个Comparable的键,保证每个结点的键都大于其左子树中任意结点的键而小于其右子树的任意结点 ...

  5. 二叉查找树BST 模板

    二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单.   根据BST的性质可以很好的解决这些东 ...

  6. Go基础之--排序和查找操作

    排序操作主要都在sort包中,导入就可以使用了import("sort") 常用的操作 sort.Ints:对整数进行排序sort.Strings:对字符串进行排序sort.Flo ...

  7. MongoDB学习(查找文档和其他数据查找操作)

    理解Cursor对象和查询运算符 cursor对象 cursor对象相当于一个指针,可通过迭代它来访问MongdoDB数据库中的一组对象. 在使用 find() 方法查询时,返回的并非实际文档,而是一 ...

  8. K:二叉查找树(BST)

    相关介绍:  二叉查找树(英语:Binary Search Tree),也称二叉搜索树.有序二叉树(英语:ordered binary tree),排序二叉树(英语:sorted binary tre ...

  9. 命题作文:在一棵IPv4地址树中彻底理解IP路由表的各种查找过程

    这是一篇命题作文.近期一直想写点东西,但一直找不到题目.正好收到一封邮件,有人问我Linux路由表的布局问题以及路由缓存的问题,加之前些日子又帮人做了一个片上路由表,所以认为这是个好题目,索性花了多半 ...

  10. 二叉查找树(BST)

    二叉查找树(BST):使用中序遍历可以得到一个有序的序列

随机推荐

  1. Redis数据库常见命令

    Redis数据库常见命令 Linux启动Redis # 启动服务 redis-server # 开启客户端 redis-cli # 关闭redis服务 shutdown #查看服务是否运行 ping ...

  2. argmax经过sigmoid和不经过sigmoid区别

    起因 今天和同组讨论了一下网络输出时,在torch.argmax之前经过torch.sigmoid和不经过sigmoid的区别. 主要起因是实验结果图像不同 图1 不经过sigmoid 图2 经过si ...

  3. 2023年8月中国数据库排行榜:TiDB 重夺榜眼,PolarDB 再进一位

    斗力频催鼓.争都更少筹. 2023年8月的 墨天轮中国数据库流行度排行 在炎炎夏日中火热出炉,本月共有286个数据库参与排名.本月排行榜前十中,头部变动加剧.TiDB 发奋图强重夺榜眼,阿里云Pola ...

  4. 小程序把html代码渲染到页面上 rich-text

    <rich-text nodes="<h1>html的代码显示再小程序使用rich-text组件</h1>"></rich-text> ...

  5. Nuxt.js 应用中的 kit:compatibility 事件钩子详解

    title: Nuxt.js 应用中的 kit:compatibility 事件钩子详解 date: 2024/10/11 updated: 2024/10/11 author: cmdragon e ...

  6. 云原生周刊:K8s 在 v1.27 中移除的特性和主要变更

    文章推荐 K8s 在 v1.27 中移除的特性和主要变更 随着 Kubernetes 发展和成熟,为了此项目的整体健康,某些特性可能会被弃用.移除或替换为优化过的特性.基于目前在 v1.27 发布流程 ...

  7. appium-命令行启动appium-server(windows)

    首先找到build\lib\main.js所在位置,我用的工具是everything(推荐一波),比windows自带的搜索强太多 执行命令:node C:\Users\Acer\AppData\Ro ...

  8. 使用notepad++批量在每行首尾添加内容

    1 简介 在程序员开发过程中,一个不错的工具是notepad++,该工具为notepad的增强,增强了许多的功能,包括程序员喜欢的列块编辑模式,支持众多的插件,例如json格式化,支持markdown ...

  9. k8s 中的 Gateway API 的背景和简介【k8s 系列之四】

    〇.Gateway API 的背景 第一阶段:Service 初始的 Kubernetes 内部服务向外暴露,使用的是自身的 LoadBlancer 和 NodePort 类型的 Service. 在 ...

  10. 基于Material Design风格开源、免费的WinForms UI控件库

    前言 今天大姚给大家分享一个基于 Google 的 Material Design 风格开源.免费的.NET WinForms UI控件库:MaterialSkin. WinForms介绍 WinFo ...