技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTALG

Kth Smallest Element in a BST

所属馆藏
Algorithm
文件格式
Markdown
原始路径
Algorithm/8-01_Kth Smallest Element in a BST_二叉搜索树中第 K 小的元素
本文目录12 个章节
  1. 题目 / Problem
  2. 示例 / Examples
  3. 约束 / Constraints
  4. 核心性质:BST 的中序遍历有序 / Key Property: BST Inorder Is Sorted
  5. 解题思路:迭代中序遍历 / Approach: Iterative Inorder Traversal
  6. 栈的作用 / Role of the Stack
  7. JavaScript 实现 / JavaScript Implementation
  8. 执行过程 / Walkthrough
  9. 递归写法 / Recursive Version
  10. 进阶:频繁修改和查询 / Follow-up: Frequent Updates and Queries
  11. 复杂度 / Complexity
  12. 易错点 / Common Pitfalls

Kth Smallest Element in a BST(二叉搜索树中第 K 小的元素)

题目 / Problem

中文: 给定一棵二叉搜索树的根节点 root 和一个整数 k,返回树中所有节点值的第 k 小元素。k1 开始计数。

English: Given the root of a binary search tree and an integer k, return the kth smallest value among all node values. k is 1-indexed.

示例 / Examples

Example 1

Input:  root = [3,1,4,null,2], k = 1
Output: 1

二叉搜索树中第一个最小值为 1 / The first smallest value in the BST is 1

中序遍历结果为 [1,2,3,4],第 1 个元素是 1
The inorder traversal is [1,2,3,4], whose first element is 1.

Example 2

Input:  root = [5,3,6,2,4,null,null,1], k = 3
Output: 3

二叉搜索树中第三个最小值为 3 / The third smallest value in the BST is 3

中序遍历结果为 [1,2,3,4,5,6],第 3 个元素是 3
The inorder traversal is [1,2,3,4,5,6], whose third element is 3.

约束 / Constraints

  • 树中节点数量为 n
    The tree contains n nodes.
  • 1 <= k <= n <= 10^4
  • 0 <= Node.val <= 10^4

核心性质:BST 的中序遍历有序 / Key Property: BST Inorder Is Sorted

二叉搜索树满足:
A binary search tree satisfies:

左子树节点值 < 根节点值 < 右子树节点值
left-subtree values < root value < right-subtree values

中序遍历的访问顺序是:
Inorder traversal visits nodes in this order:

左子树 → 根节点 → 右子树
Left subtree → Root → Right subtree

因此,对 BST 进行中序遍历会按从小到大的顺序访问节点。第 k 个访问到的节点就是第 k 小元素。
Therefore, inorder traversal of a BST visits values from smallest to largest. The kth visited node is the kth smallest value.

解题思路:迭代中序遍历 / Approach: Iterative Inorder Traversal

使用栈模拟递归中序遍历:
Use a stack to simulate recursive inorder traversal:

  1. 从当前节点不断向左移动,并将沿途节点压入栈。
    Move left repeatedly, pushing every visited node onto the stack.
  2. 无法继续向左时,弹出栈顶节点。
    When no further left move is possible, pop the top node.
  3. 该节点是当前尚未访问节点中的最小值,计数加一。
    This node is the smallest unvisited value, so increment the visit count.
  4. 如果计数等于 k,直接返回该节点值。
    If the count equals k, return the node's value immediately.
  5. 否则转向该节点的右子树,重复以上过程。
    Otherwise, move to its right subtree and repeat.

算法不需要生成完整的有序数组,找到第 k 个节点后即可提前停止。
There is no need to construct the complete sorted array; stop as soon as the kth node is reached.

栈的作用 / Role of the Stack

栈保存“已经到达但还不能访问”的祖先节点:
The stack stores ancestors that have been reached but cannot yet be visited:

        5
       /
      3
     /
    2
   /
  1

向左下降后的栈 / Stack after descending left:
[5, 3, 2, 1]

弹出顺序从最左节点开始,因此符合中序遍历的升序要求。
Popping begins with the leftmost node, preserving the ascending inorder order.

JavaScript 实现 / JavaScript Implementation

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *   this.val = val ?? 0;
 *   this.left = left ?? null;
 *   this.right = right ?? null;
 * }
 */

/**
 * @param {TreeNode} root
 * @param {number} k
 * @return {number}
 */
function kthSmallest(root, k) {
  const stack = [];
  let current = root;
  let visited = 0;

  while (current !== null || stack.length > 0) {
    while (current !== null) {
      stack.push(current);
      current = current.left;
    }

    current = stack.pop();
    visited++;

    if (visited === k) {
      return current.val;
    }

    current = current.right;
  }

  // 题目保证 1 <= k <= 节点数量,因此不会执行到这里。
  // Constraints guarantee a valid k, so this line is unreachable.
  return -1;
}

执行过程 / Walkthrough

使用 Example 2:
Using Example 2:

        5
       / \
      3   6
     / \
    2   4
   /
  1

中序访问过程:
Inorder visits:

访问次序 / Visit节点值 / Value是否达到 k = 3 / Reached k?
11否 / No
22否 / No
33是 / Yes,返回 3 / return 3

节点 456 不需要继续访问。
Nodes 4, 5, and 6 do not need to be visited.

递归写法 / Recursive Version

也可以使用递归中序遍历,并在找到答案后停止继续搜索:
A recursive inorder traversal can also stop after finding the answer:

function kthSmallest(root, k) {
  let remaining = k;
  let answer = -1;

  function inorder(node) {
    if (node === null || remaining === 0) {
      return;
    }

    inorder(node.left);

    if (remaining === 0) {
      return;
    }

    remaining--;

    if (remaining === 0) {
      answer = node.val;
      return;
    }

    inorder(node.right);
  }

  inorder(root);
  return answer;
}

迭代版本不会受到 JavaScript 递归调用栈深度的限制,更适合可能严重偏斜的树。
The iterative version avoids JavaScript recursion-depth limits and is safer for highly skewed trees.

进阶:频繁修改和查询 / Follow-up: Frequent Updates and Queries

如果 BST 经常进行插入、删除,并且频繁查询第 k 小元素,可以为每个节点维护:
If the BST is frequently updated and queried, augment every node with:

subtreeSize = 以当前节点为根的子树节点数量
              number of nodes in the current node's subtree

查询时,设左子树大小为 leftSize
During a query, let leftSize be the size of the left subtree:

  • 如果 k === leftSize + 1,当前节点就是答案。
    If k === leftSize + 1, the current node is the answer.
  • 如果 k <= leftSize,答案位于左子树。
    If k <= leftSize, search the left subtree.
  • 如果 k > leftSize + 1,答案位于右子树,并令 k -= leftSize + 1
    If k > leftSize + 1, search the right subtree with k -= leftSize + 1.
function kthSmallestWithSize(root, k) {
  let current = root;

  while (current !== null) {
    const leftSize = current.left?.subtreeSize ?? 0;

    if (k === leftSize + 1) {
      return current.val;
    }

    if (k <= leftSize) {
      current = current.left;
    } else {
      k -= leftSize + 1;
      current = current.right;
    }
  }

  return -1;
}

插入或删除节点时,需要沿搜索路径更新所有祖先节点的 subtreeSize
When inserting or deleting a node, update subtreeSize for every ancestor along the search path.

如果使用 AVL 树、红黑树等平衡 BST,并维护子树大小:
With a balanced BST such as an AVL or red-black tree augmented with subtree sizes:

  • 插入 / Insert: O(log n)
  • 删除 / Delete: O(log n)
  • 查询第 k 小 / Kth-smallest query: O(log n)

这种结构也称为顺序统计树(Order Statistic Tree)。
This augmented structure is also called an Order Statistic Tree.

复杂度 / Complexity

设树高为 h
Let h be the tree height.

  • 时间复杂度 / Time: O(h + k),遍历到第 k 个节点后停止;最坏为 O(n)
    O(h + k) before stopping at the kth node; O(n) in the worst case.
  • 空间复杂度 / Space: O(h),用于显式栈。
    O(h) for the explicit stack.

平衡树中 h = O(log n),完全偏斜的树中 h = O(n)
For a balanced tree, h = O(log n); for a completely skewed tree, h = O(n).

易错点 / Common Pitfalls

  • k1 开始计数,不是从 0 开始。
    k is 1-indexed, not 0-indexed.
  • 必须使用中序遍历“左 → 根 → 右”,不能使用前序或后序遍历。
    Use inorder traversal left-root-right, not preorder or postorder.
  • 找到第 k 个节点后可以立即返回,不需要遍历完整棵树。
    Return immediately after reaching the kth node; do not traverse the whole tree unnecessarily.
  • 迭代写法的外层条件是 current !== null || stack.length > 0
    The iterative outer condition is current !== null || stack.length > 0.
  • 从栈中弹出节点并访问后,应转向它的右子树。
    After popping and visiting a node, move to its right subtree.
  • 进阶方案中,插入、删除和树旋转后都必须正确维护 subtreeSize
    In the augmented solution, maintain subtreeSize after insertions, deletions, and tree rotations.
457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS