news 2026/9/19 14:20:45

LeetCode 0700 二叉搜索树搜索(Search in a Binary Search Tree)多语言题解:递归与迭代两种实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 0700 二叉搜索树搜索(Search in a Binary Search Tree)多语言题解:递归与迭代两种实现

LeetCode 0700 二叉搜索树搜索(Search in a Binary Search Tree)多语言题解:递归与迭代两种实现

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

本文围绕 LeetCode 0700「Search in a Binary Search Tree」展开,基于当前仓库的 题解文档 与其配套源码,系统讲解在二叉搜索树(BST)中查找目标节点的两种标准写法——递归与迭代。读完本文,你将掌握利用 BST 有序性将查找复杂度收敛到 O(H)(H 为树高)的核心思路,理解两种写法的复杂度差异与适用场景,并能直接套用仓库中 Python、Java、C++、JavaScript、C#、Go、Kotlin、Swift、Rust 九种语言的现成实现。


前置知识:解题前需要掌握的概念

原文档要求读者在动手之前先熟悉以下三个基础,这也是几乎所有 BST 类问题(如 插入节点、验证二叉搜索树)的公共前提:

  • 二叉搜索树(BST)性质:对于任意节点,其左子树的所有值都小于该节点值,右子树的所有值都大于该节点值。这是整个搜索过程能够"每次排除一半子树"的理论根基。
  • 树遍历:能够通过 left/right 孩子指针在节点间移动,理解沿路径下降的过程。
  • 递归:能够用递归函数处理树结构,并正确设置基线条件(base case)来终止递归。

这三个概念分别对应了本问题的三个关键词:凭什么能二分(BST 性质)、怎么走(指针/遍历)、怎么写(递归或循环)。


1. 递归解法

直觉(Intuition)

BST 的特殊性质决定了搜索策略:对于当前节点,如果目标值更小,那么目标只可能出现在左子树;如果更大,只可能出现在右子树。因此每一步都可以丢弃一半的搜索空间。搜索过程要么在某层找到值相等的节点并返回它,要么一路走到null指针——此时说明树中不存在该值,返回null

算法步骤(Algorithm)

  1. rootnull,或root.val等于目标值,直接返回root
  2. 若目标值小于root.val,递归搜索左子树。
  3. 否则,递归搜索右子树。
  4. 返回递归调用的结果。

多语言实现

以下是原文档中给出的递归版本完整实现,涵盖仓库支持的主流语言(Python / Java / C++ / JavaScript / C# / Go / Kotlin / Swift / Rust):

# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: if not root or root.val == val: return root return self.searchBST(root.left, val) if val < root.val else self.searchBST(root.right, val)
public class Solution { public TreeNode searchBST(TreeNode root, int val) { if (root == null || root.val == val) { return root; } return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val); } }
class Solution { public: TreeNode* searchBST(TreeNode* root, int val) { if (!root || root->val == val) { return root; } return val < root->val ? searchBST(root->left, val) : searchBST(root->right, val); } };
class Solution { /** * @param {TreeNode} root * @param {number} val * @return {TreeNode} */ searchBST(root, val) { if (!root || root.val === val) { return root; } return val < root.val ? this.searchBST(root.left, val) : this.searchBST(root.right, val); } }
public class Solution { public TreeNode SearchBST(TreeNode root, int val) { if (root == null || root.val == val) { return root; } return val < root.val ? SearchBST(root.left, val) : SearchBST(root.right, val); } }
func searchBST(root *TreeNode, val int) *TreeNode { if root == nil || root.Val == val { return root } if val < root.Val { return searchBST(root.Left, val) } return searchBST(root.Right, val) }
class Solution { fun searchBST(root: TreeNode?, `val`: Int): TreeNode? { if (root == null || root.`val` == `val`) { return root } return if (`val` < root.`val`) searchBST(root.left, `val`) else searchBST(root.right, `val`) } }
class Solution { func searchBST(_ root: TreeNode?, _ val: Int) -> TreeNode? { guard let root = root else { return nil } if root.val == val { return root } return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val) } }
impl Solution { pub fn search_bst( root: Option<Rc<RefCell<TreeNode>>>, val: i32, ) -> Option<Rc<RefCell<TreeNode>>> { match root { None => None, Some(node) => { let n = node.borrow(); if n.val == val { drop(n); Some(node) } else if val < n.val { Self::search_bst(n.left.clone(), val) } else { Self::search_bst(n.right.clone(), val) } } } } }

说明:Rust 由于TreeNode使用Option<Rc<RefCell<TreeNode>>>包装,需要通过borrow()读取节点值,并在返回值前drop借用;递归调用时克隆左右子树引用。这是 Rust 所有权模型下处理树的典型写法。

仓库源码的另一种递归风格

仓库中还收录了与上面略有差异的递归实现,例如 java/0700-search-in-a-binary-search-tree.java:

public TreeNode searchBST(TreeNode root, int val) { if (root == null) { return root; } else if (root.val < val) { return searchBST(root.right, val); } else if (root.val > val) { return searchBST(root.left, val); } return root; }

这种写法把三种情况显式拆开:先判空、再判断"目标更大→向右""目标更小→向左",最后剩下的情况即root.val == val,返回root。swift/0700-search-in-a-binary-search-tree.swift 也采用了同样的先判空、再比较大小的结构:

class Solution { func searchBST(_ root: TreeNode?, _ val: Int) -> TreeNode? { if root == nil { return nil } if val > root!.val { return searchBST(root?.right, val) } else if val < root!.val { return searchBST(root?.left, val) } else { return root } } }

两种递归风格逻辑完全等价,区别仅在于比较顺序与返回写法:合并式把"为空或命中"合并为一个基线条件,拆分式则显式枚举三种分支。面试中两种都可接受,建议按自己最不易出错的习惯选择。

复杂度分析

  • 时间复杂度:O(H),其中 H 为给定树的高度。每一步只沿一条路径下降,不会回溯。
  • 空间复杂度:O(H),来自递归调用栈的深度。

注意这里的 H 是树高。在平衡 BST 中 H ≈ log₂N,此时时间复杂度近似 O(log N);但在退化成链的树中 H = N,最坏为 O(N)。原文档统一用 O(H) 表述,是最严谨的写法。


2. 迭代解法

直觉(Intuition)

递归的本质是借助调用栈保存"剩余工作"。本问题的搜索路径是单条的——每一步只有唯一的去向(左或右),因此完全可以用一个while循环模拟递归过程:用局部变量代替调用栈。这样既避免了递归调用本身的函数栈开销,也把额外空间降到常数级。

算法步骤(Algorithm)

  1. root不为nullroot.val不等于目标值时,循环执行:
    • 若目标值小于root.val,将当前节点更新为root.left
    • 否则更新为root.right
  2. 循环结束后返回root——它要么是找到的目标节点,要么是null(表示未找到)。

多语言实现

以下是原文档给出的迭代版本完整实现:

class Solution: def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: while root and root.val != val: root = root.left if val < root.val else root.right return root
public class Solution { public TreeNode searchBST(TreeNode root, int val) { while (root != null && root.val != val) { root = val < root.val ? root.left : root.right; } return root; } }
class Solution { public: TreeNode* searchBST(TreeNode* root, int val) { while (root && root->val != val) { root = val < root->val ? root->left : root->right; } return root; } };
class Solution { /** * @param {TreeNode} root * @param {number} val * @return {TreeNode} */ searchBST(root, val) { while (root != null && root.val != val) { root = val < root.val ? root.left : root.right; } return root; } }
public class Solution { public TreeNode SearchBST(TreeNode root, int val) { while (root != null && root.val != val) { root = val < root.val ? root.left : root.right; } return root; } }
func searchBST(root *TreeNode, val int) *TreeNode { for root != nil && root.Val != val { if val < root.Val { root = root.Left } else { root = root.Right } } return root }
class Solution { fun searchBST(root: TreeNode?, `val`: Int): TreeNode? { var node = root while (node != null && node.`val` != `val`) { node = if (`val` < node.`val`) node.left else node.right } return node } }
class Solution { func searchBST(_ root: TreeNode?, _ val: Int) -> TreeNode? { var node = root while node != nil && node!.val != val { node = val < node!.val ? node!.left : node!.right } return node } }
impl Solution { pub fn search_bst( root: Option<Rc<RefCell<TreeNode>>>, val: i32, ) -> Option<Rc<RefCell<TreeNode>>> { let mut cur = root; while let Some(node) = cur { let n = node.borrow(); if n.val == val { drop(n); return Some(node); } else if val < n.val { cur = n.left.clone(); } else { cur = n.right.clone(); } } None } }

迭代版本各语言要点:

  • Kotlin / Swift 需要引入一个var可变变量(如node/cur)来在循环中不断移动"当前指针",因为原入参在 Kotlin 中为只读引用。
  • Swift 在循环体内使用node!强制解包是安全的,因为循环条件已保证node != nil
  • Rust 版本在命中时先drop(n)释放借用再返回Some(node),避免借用冲突;未命中则沿left/right继续,循环结束后返回None

复杂度分析

  • 时间复杂度:O(H),与递归版本相同,H 为树高。
  • 空间复杂度:O(1)额外空间,因为没有使用调用栈,只占用常量级的指针变量。

递归 vs 迭代如何选择

维度递归迭代
时间复杂度O(H)O(H)
空间复杂度O(H)(调用栈)O(1)
代码风格简洁、与 BST 定义一一对应稍显过程化,但更省内存
适用场景思路讲解、树高可控(平衡 BST)树高较大、担心栈溢出时优先

对于 0700 这道题,两种写法都很短;工程上如果树可能退化为长链(H 接近 N),迭代的 O(1) 空间优势会体现为不会触发递归栈溢出。


常见陷阱(Common Pitfalls)

陷阱一:忽略 BST 性质,当成普通二叉树搜索

如果把这道题当成普通二叉树搜索——在每个节点同时探查左右两个孩子——就完全失去了 BST 结构的意义。BST 性质保证目标值只可能出现在其中一个子树:值小于当前节点就去左边,大于就去右边。任何"两边都查"的写法都会把复杂度从 O(H) 恶化到 O(N),并且没有利用题目给出的有序结构。判断方向永远只需要一次valroot.val的比较。

陷阱二:忘记处理 null 情况

在访问root.val之前不检查root是否为null,会导致空指针异常(null pointer exception)。这个检查有两重身份:

  • 在递归写法中,它是基线条件:树中找不到目标值、一路走到叶子之下时,null就是递归的终止点;
  • 在迭代写法中,它是循环终止条件while (root != null && root.val != val)中的root != null保证循环安全退出。

原文档中的两份参考实现(Java 版、Swift 版)也都把判空放在第一行,可见这是所有正确实现共有的前提。


延伸:本题在 BST 系列中的位置

0700 是 BST 系列里最基础的一道"读操作",与仓库中的其他 BST 文章形成完整的知识闭环:

  • 学会搜索(本题)之后,可继续学习 向 BST 插入节点:插入算法复用了完全相同的"比较大小→决定方向→走到 null 为止"的框架,区别仅在于到达空位时是"返回 null"还是"新建节点";
  • 反过来,验证二叉搜索树 则考察你是否真正理解 BST 性质:它要求用区间约束(最小值/最大值)递归校验整棵树,是 0700 反向思维的高级形态。

建议按「搜索 → 插入 → 验证」的顺序刷题,三题合起来能让你把 BST 的"读、写、校验"三种基本操作一次性打通。


小结

关键点结论
核心思想利用 BST 有序性,每次比较后只进入一个子树
递归写法基线条件(null 或命中)+ 单向递归,时间 O(H)、空间 O(H)
迭代写法while 循环模拟下降,时间 O(H)、空间 O(1)
两个坑不要两边都搜;访问val前必须判空
仓库参考题解文档、Java 实现、Swift 实现

本题虽然只有十几行代码,却是理解 BST 全部后续算法(插入、删除、验证、最近公共祖先等)的基石。无论你用哪种语言、哪种写法,只要牢牢抓住"每次只走一条路"这一条主线,就不会出错。

【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 14:17:55

从npm publish到npx使用:命令行工具发布实战与踩坑记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 14:17:27

Win11本地部署OpenClaw全链路指南:WSL2+Docker+GPU加速实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 14:15:47

RIOT 中 LPSXXX 气压传感器驱动:从测试应用到寄存器级实现解析

物联网嵌入式操作系统实时系统 【免费下载链接】RIOT RIOT - The friendly OS for IoT 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/riot/RIOT 点击查看 免费下载 导读 本文围绕 RIOT&#xff08;The friendly OS for IoT&#xff09;中 LPSXXX 系列气压传感器…

作者头像 李华
网站建设 2026/9/19 14:14:30

从GPT-3训练算力测算看AI服务器硬件配置与选型逻辑

简介&#xff1a;这是一份聚焦2023年AI服务器市场的行业分析报告&#xff0c;面向算力基础设施、IT硬件及人工智能相关领域的从业者、研究者和投资者。报告基于Counterpoint、IDC等机构数据&#xff0c;指出2022年全球服务器出货量约1380万台、收入1117亿美元&#xff0c;并分析…

作者头像 李华
网站建设 2026/9/19 14:13:20

数字孪生+风景园林:从倾斜摄影到积温驱动的季相演算

简介&#xff1a;这是一份PDF学术资料&#xff0c;围绕数字孪生技术在风景园林设计中的应用展开&#xff0c;适合风景园林设计师、研究人员以及智慧城市相关从业者阅读。内容从数字孪生技术概述切入&#xff0c;重点阐述其与LIM风景园林信息模型的融合路径&#xff0c;强调实时…

作者头像 李华
网站建设 2026/9/19 14:12:49

Open5GS在Ubuntu 22.04上的5G核心网实战部署指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华