深入理解二叉查找树(BST)的重要查找操作

news/2024/12/23 18:28:25/文章来源:https://www.cnblogs.com/ofnoname/p/18624757

二叉查找树 (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) $ 的级别。作为延伸思考,你可以研究平衡树的原理及其在保持效率上的作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/857579.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VXLAN 网络中报文转发过程

以同网段的 VM 间互通简单介绍 VXLAN 网络中的报文转发过程。本文分享自天翼云开发者社区《VXLAN 网络中报文转发过程》,作者:刘****林 以同网段的 VM 间互通简单介绍 VXLAN 网络中的报文转发过程。1.VM1 发送目的地址为 VM2 的报文。 2.VTEP1 收到该报文后进行 VXLAN 封装,…

【YashanDB知识库】YMP迁移过程中报错YAS-02143或YAS-02193

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802944.html?templateId=1718516 【标题】YMP迁移过程中报错account lock 【关键字】YMP 迁移 account lock openssl 【问题描述】YMP所在机器的openssl版本低于1.1.1,在安装YMP及内置库时使用了…

js侧边栏菜单插件canvi

canvi.js是一款js侧边栏菜单插件。canvi.js使用简单,可以快速的制作出侧边栏展开收缩js特效。你可以在同一个页面实例化多个侧边栏实例,还可以设置侧边栏的响应式宽度,自定义侧边栏样式等。在线预览 下载使用方法 在页面中引入canvi.css和canvi.js文件。< link rel=&qu…

GitHub主页3D图表显示

效果如图,项目来源于 yoshi389111/github-profile-3d-contrib: This GitHub Action creates a GitHub contribution calendar on a 3D profile image. 1. 创建自己的github主页属性项目——跟你github用户名一致即可, 比如我github名字叫FlameskyDexive, 创建一个FlameskyDe…

E. Expression Correction

链接:https://codeforces.com/contest/2052/problem/E 题目:思路: 一道模拟。重点在于:移动每个数字;判断是否成立。 问题一:选中每个数码,枚举需要移动到的位置,使用swap函数。 问题二:格式问题+算术问题。不能有前导0,不能两个非数字相连,同时位数不能超过十位,首位和…

zabbix外部程序告警关键配置

环境: OS:Centos 7 zabbix:4.0.51.主机配置 2.用户配置 3.用户组配置 4.动作配置 5.脚本需要加上执行权限chown +x 脚本

Java项目实战之基于 Spring Boot、MyBatis 和 Vue.js 的智能停车场系统设计与技术选型

1. 系统概述 本智能停车场系统旨在为停车场提供高效、便捷的管理解决方案,涵盖车辆进出管理、车位预订、停车费用计算、用户信息管理等功能,同时提供管理员操作界面和用户移动端应用,提升停车场运营效率和用户体验。 1.1目标 实现停车场自动化管理,提高车位利用率,减少人工…

vmagent如何快速采集和转发Metrics

vmagent如何快速采集和转发Metrics本文介绍了vmagent的设计细节,参考自:vmagent-how-it-worksVictoriaMetrics agent是一个轻量级工具,用于采集不同源的指标。vmagent可以在转发指标前(通过"relabeling")定制指标(降低基数、流聚合、去重等)。第一步:通过API或抓…

SwiftUI 2024 All In One

SwiftUI 2024 All In OneSwiftUI 2024 All In OneWWDC24 SwiftUI & UI Frameworks guidehttps://developer.apple.com/news/?id=zqzlvxlm demos Design and build your apps like never before. 以前所未有的方式设计和构建您的应用程序。 With enhancements to live prev…

IGM机器人K5齿轮箱维修故障详情介绍

在长期、高强度的工作中,IGM机器人K5齿轮箱难免会出现故障,需要联系子锐机器人维修进行及时的维修和保养。 一、齿轮磨损 齿轮磨损是IGM机器人K5齿轮箱最常见的故障之一。长时间、高速运转以及负载的频繁变化都会导致齿轮表面的磨损。磨损的齿轮会降低传动效率,产生噪音,甚…

【NAS】绿联NAS+极狐Gitlab+1Panel

1. 准备域名 例如我的 ???.mllt.cc 2. 内网穿透 我使用的Natfrp(https://www.natfrp.com/tunnel/) 创建HTTP隧道(对应端口10080)创建HTTP隧道(对应端口10443) 注意这两隧道要一致,以便绑定同一个域名???.mllt.cc 然后在域名解析那里,将???.mllt.cc解析到隧道地…