二叉查找树 (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. 2024年9月中国数据库排行榜:openGauss系多点开花,根社区优势明显

    在墨天轮发布的9月中国数据库流行度排行榜中,中国数据库产业格局进一步聚集刷新,呈现出3大显著特征: 开源势力力争上游显优势领先潮流: openGauss 开源根社区优势明显: 阿里华为两极鼎立云上云下 ...

  2. vue前端开发仿钉图系列(7)底部数据列表的开发详解

    底部数据列表主要是记录图层下面对应的点线面数据,点击单元行或者查看或者编辑,弹出右侧编辑页面,点击单元行地图定位到相应的绘图位置.里面的难点1是动态绑定字段管理编辑的字段以及对应的value值,2是点 ...

  3. 创建一个简单的基于MyBatis的项目

  4. KubeSphere + Argo CD,实现真正的 GitOps!

    来自社区用户 willqy 的分享 Argo CD 简介 Argo CD 是用于 Kubernetes 的声明性 GitOps 持续交付工具,应用程序定义,配置和环境应为声明性的,并应受版本控制,应用 ...

  5. python项目实战——人生重开模拟器

    文章目录 1.菜单栏的编写 2.玩家确定颜值.体质.智力.家境 3.生成性别 4.设定角色出生点 5.各个年龄段的变化 5.1 幼年阶段 5.2 青年阶段 5.3中年阶段 5.4 晚年阶段 6.整体代 ...

  6. Nginx 安全配置

    server { listen 8089; server_name 10.5.210.203:8089; #charset koi8-r; #access_log logs/host.access.l ...

  7. git reset 之后切换到原来的commit

    git reset的语法: git reset [--hard|soft|mixed|merge|keep] [<commit>或HEAD] 作用:将当前分支reset到指定的commit ...

  8. 使用免费的SVN服务器

    在本地环境中安装SVN window版本自行搜索图文教程. linux版本(以我的 ubuntu 为例子) 我尝试直接敲SVN svn 报错 Command 'svn' not found, but ...

  9. Stratum挖矿协议&XMR挖矿流量分析

    目录 前言 区块链和挖矿相关概念 挖矿木马 挖矿协议Stratum Stratum工作过程 XMR挖矿流量分析 环境搭建 流量分析 总结 前言 之前参与了一个关于"挖矿行为检测"的 ...

  10. 外网如何通过https访问自己的服务

    前情提要 最近在接wx小游戏的sdk, 消息推送需要在wx端配置https开头的地址. 我本地开了一个http服务,我的ip为192.168.40.39, 监听端口 8091.想要达到的效果是,外部浏 ...