这是 leetcode 面试刷题一题多解系列的第10篇,练习分别使用递归和迭代的方式查找二叉树的最近公共祖先。
题目
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
来源:力扣(LeetCode)
链接:leetcode.cn/problems/lo…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路解析
二叉搜索树的性质是,左子树的所有节点的值都小于根节点的值,右子树的所有节点的值都大于根节点的值。因此,在二叉搜索树中查找某个节点时,我们可以根据节点的值与当前根节点的值的大小关系,来判断要查找的节点在左子树中还是右子树中。
根据二叉搜索树的性质,可以使用递归的方式查找最近公共祖先。首先,比较 p 和 q 的值与当前根节点的值的大小关系,如果都小于当前根节点的值,那么最近公共祖先一定在当前节点的左子树中;如果都大于当前根节点的值,那么最近公共祖先一定在当前节点的右子树中;如果一个大于当前根节点的值,一个小于当前根节点的值,那么当前节点就是它们的最近公共祖先。如果当前节点为空,说明没有找到 p 或 q 中的一个,返回 null。
题解1---递归
递归的过程可以通过函数实现。函数的参数是当前节点 root、要查找的节点 p 和 q。函数的返回值是最近公共祖先节点。递归的结束条件是当前节点为空或者找到了 p 或 q 中的一个。
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
if (root === null) {
return null;
}
if (root.val > p.val && root.val > q.val) {
return lowestCommonAncestor(root.left, p, q);
} else if (root.val < p.val && root.val < q.val) {
return lowestCommonAncestor(root.right, p, q);
} else {
return root;
}
};
题解2---迭代
除了递归,我们还可以使用迭代的方式查找最近公共祖先。与递归不同的是,迭代过程中需要维护一个栈来记录访问过的节点,以及它们的父节点。
迭代的过程中,我们可以使用一个 while 循环,不断地将当前节点 root 和要查找的节点 p、q 进行比较,然后根据比较结果选择向左或向右遍历。遍历的过程中,将访问的节点和它们的父节点分别入栈。当遍历到 p 或 q 中的一个节点时,就停止遍历,并返回它的祖先节点。
var lowestCommonAncestor = function(root, p, q) {
let stack = [root];
let parent = new Map();
parent.set(root, null);
while (!parent.has(p) || !parent.has(q)) {
let node = stack.pop();
if (node.left !== null) {
parent.set(node.left, node);
stack.push(node.left);
}
if (node.right !== null) {
parent.set(node.right, node);
stack.push(node.right);
}
}
let ancestors = new Set();
while (p !== null) {
ancestors.add(p);
p = parent.get(p);
}
while (!ancestors.has(q)) {
q = parent.get(q);
}
return q;
};
这两种方法都可以在时间复杂度和空间复杂度上达到 O(h),其中 h 是二叉搜索树的高度。递归的代码简洁明了,易于理解和实现,而迭代的方法更加直观,可以更好地掌握栈的使用技巧。
我的更多前端资讯
欢迎大家技术交流 资料分享 摸鱼 求助皆可 —链接