二叉查找树 (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. USB协议详解第1讲(核心概念通俗理解)

    0.概括 USB协议学习中最重要几个概念如下,没有提及的就是对USB协议学习中不重要的或者编程不需要用到的.大家也不用着急,概念必须要学会,否则都不知道下面这些东西是什么还学什么通用串行总线协议,大家 ...

  2. ssh建立github连接 基于ssh密钥

    1. 建立公钥和私钥 ps:公钥放在github上面的,私钥放在自己本地电脑 : 先生成密钥:打开 gitbash 输入命令: ssh-keygen -t rsa -b 4096 -C "z ...

  3. 东方通信基于 KubeSphere 的云计算落地经验

    作者:周峰 吴昌泰 公司简介 东方通信股份有限公司(以下简称"东方通信")创立于 1958 年,是一家集硬件设备.软件.服务为一体的整体解决方案提供商.公司于 1996 年成功改制 ...

  4. KubeSphere 社区征稿说明

    KubeSphere 社区一直致力于云原生技术的布道工作.通过组织线下 Meetup,线上的定期技术直播,KubeSphere 社区输出了颇多精彩的技术分享内容.此外,社区还产出了多篇优质的技术文章, ...

  5. 如何使用 VuePress 搭建博客网站并 Vercel 部署

    先来看一下网站截图: 快速上手 1.创建并进入一个新目录 mkdir vuepress-starter && cd vuepress-starter 2.使用你喜欢的包管理器进行初始化 ...

  6. docker命令总结(一)

    个人简单总结: 参数 用途 语法 示例 search 在docker hub中搜索镜像 docker search 镜像名称 docker search nginx pull 在docker hub中 ...

  7. linux 基础(1)快速查询指令的用法

    --help 几乎所有的指令,都可以使用--help选项进行查询.给命令使用--help选项,就会直接出现一段说明命令的文字. > date --help 用法:date [选项]... [+格 ...

  8. vue之计算属性computed模板

    计算属性:故名思意也是一种属性,可以用插值表达式直接调用 废话不多说,直接上代码: 页面部分 <!-- 用户名下拉菜单 --> <el-dropdown class="us ...

  9. SICTF 2024 Round4 Crypto

    SICTF-Round4--Crypto SignBase task: U0lDVEZ7ODI5MGYwZWYtNzAyYi00NTZmLTlmZjYtNGRhZjhhYTIzNWU1fQ== exp ...

  10. 超级干货!Air780E的串口通信分享

    ​ 猛然发现,Air780E的串口通信还没分享,难怪已经有小伙伴提出了要求! 那我们来讲解低功耗4G模组Air780E的串口通信的基本用法,小伙伴们,学起来吧! 一.硬件准备  ​ 780E开发板一套 ...