引言
二叉搜索树中第 K 小的元素
- 🎈 题目链接:
- 🎈 做题状态:
我的解题
这道题目最暴力的解法就是中序遍历结果保存在数组里面然后直接通过索引值返回。
但是其实可以在中序递归遍历的时候记录一下当前遍历了几个元素,可以用全局变量记录也可以用引用传参记录。
class Solution {
public:
void midTraversal(TreeNode* root, int& count, int& result, int k)
{
if (!root) return;
midTraversal(root->left, count, result, k);
// 判断当前count是否是k
if (count == k)
{
cout << "count = " << count << "; root->val = " << root->val << endl;
result = root->val;
++count;
return;
}
++count;
midTraversal(root->right, count, result, k);
}
int kthSmallest(TreeNode* root, int k) {
int result = -1;
int count = 1;
midTraversal(root, count, result, k);
return result;
}
};
分析
代码思路分析
通过中序遍历(左-根-右)来找到二叉搜索树(BST)中第 k
小的元素。BST 的中序遍历结果是一个升序序列,因此第 k
个被访问的节点就是第 k
小的元素。
具体实现:
-
递归中序遍历:
midTraversal
函数递归地遍历左子树、处理当前节点、然后递归遍历右子树。- 用
count
记录当前是第几个被访问的节点(从 1 开始计数)。 - 当
count == k
时,记录当前节点的值到result
并提前返回(避免不必要的遍历)。
-
提前终止:
- 一旦找到第
k
小的元素(count == k
),后续的递归调用会直接返回,不再继续遍历。
- 一旦找到第
参数说明:
count
:通过引用传递,记录当前遍历的节点序号。result
:通过引用传递,保存第k
小的值。k
:目标序号。
代码优化
-
减少参数传递
count
和result
可以通过成员变量或全局变量替代,减少递归函数的参数传递。但这样会牺牲一定的封装性,需权衡。 -
迭代替代递归
递归调用会有栈空间的开销,尤其是当树很高时可能导致栈溢出。可以用迭代(显式栈)实现中序遍历,同样支持提前终止。 -
简化逻辑
count
的初始值可以从 1 改为 0,这样++count
可以放在判断之前,逻辑更直观。- 不需要在
count == k
时执行++count
,因为后续已经直接返回。
优化后的代码
递归版(参数优化):
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
int count = 0;
int result = -1;
midTraversal(root, count, result, k);
return result;
}
void midTraversal(TreeNode* root, int& count, int& result, int k) {
if (!root || result != -1) return;
midTraversal(root->left, count, result, k);
if (++count == k) {
result = root->val;
return;
}
midTraversal(root->right, count, result, k);
}
};
迭代版(推荐):
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> st;
TreeNode* curr = root;
int count = 0;
while (curr || !st.empty()) {
while (curr) {
st.push(curr);
curr = curr->left;
}
curr = st.top();
st.pop();
if (++count == k) {
return curr->val;
}
curr = curr->right;
}
return -1; // 未找到(题目保证 k 有效时可省略)
}
};
关键优化点
- 迭代法:避免了递归的栈开销,显式用栈模拟中序遍历。
- 提前终止:在找到第
k
小元素后直接返回。 - 逻辑简化:
count
从 0 开始,先递增再判断。
复杂度
- 时间:平均 O(k),最坏 O(n)(当 k = n 时需要遍历所有节点)。
- 空间:递归为 O(h),迭代为 O(h)(h 是树高)。