力扣 230题 二叉搜索树中第K小的元素
给定一个二叉搜索树的根节点 root
,和一个整数 k
,请你设计一个算法查找其中第 k
个最小元素(从 1 开始计数)。
如:输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
这题一般使用中序遍历 栈与递归 的做法,但是空间复杂度会是 O(n),部分企业可能要求空间复杂度为 O(1) 的优化算法,这时应该使用 Morris 中序遍历。
栈:
// 栈
var kthSmallest = function(root, k) {
const stack = [];
while(stack.length || root != null) {
while(root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
k--;
if(k == 0) break;
root = root.right;
}
return root.val;
};
递归:
// 递归
var kthSmallest = function(root, k) {
let res = -1;
let now = 1;
const Inorder = (root) => {
if(root == null || res != -1) return;
Inorder(root.left, now);
if(now == k) res = root.val;
now++;
Inorder(root.right, now);
}
Inorder(root, 1);
return res;
};
Morris 中序遍历:
Morris 遍历算法整体步骤如下(假设当前遍历到的节点为 x)【搬运自力扣】:
力扣https://leetcode.cn/problems/binary-tree-inorder-traversal/
1. 如果 x 无左孩子,先将 x 的值加入答案数组,再访问 x 的右孩子,即 x = x.right。
2. 如果 x 有左孩子,则找到 x 左子树上最右的节点(即左子树中序遍历的最后一个节点,x 在中序遍历中的前驱节点),我们记为 predecessor。根据 predecessor 的右孩子是否为空,进行如下操作:
如果 predecessor 的右孩子为空,则将其右孩子指向 x,然后访问 x 的左孩子,即 x = x.left。
如果 predecessor 的右孩子不为空,则此时其右孩子指向 x,说明我们已经遍历完 x 的左子树,我们将 predecessor 的右孩子置空,将 x 的值加入答案数组,然后访问 x 的右孩子,即 x = x.right。
3. 重复上述操作,直至访问完整棵树。
// Morris 中序遍历
var kthSmallest = function(root, k) {
// 设置前驱节点
predecessor = null;
while(root) {
// 左子树不为空,遍历左子树
if(root.left) {
predecessor = root.left;
while(predecessor.right && predecessor.right != root) {
predecessor = predecessor.right;
}
// 不成环,将 predecessor 的右子树设为 root
if(!predecessor.right) {
predecessor.right = root;
root = root.left;
}
// 成环,说明此时左子树已经遍历完毕,准备遍历右子树
else {
predecessor.right = null;
k--;
if(k == 0) break;
root = root.right;
}
}
// 左子树为空,说明此时 root.val 为最小值,准备遍历右子树
else {
k--;
if(k == 0) break;
root = root.right;
}
}
return root.val;
};