二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
1.哈希表解法
(1)创建队列queue
进行二叉树层序遍历
(2)设置map
映射父子结点关系,从而可以从子结点访问到父节点
(3)利用map
记录的联系找出p
所有的祖宗结点存在set
中
(4)遍历q
的父节点,如果存在于set
中,则为最近公共祖先,直接返回
var lowestCommonAncestor = function (root, p, q) {
/* 如果root为p或q直接返回 */
if (!root || root === p || root === q) return root;
let queue = [root];
let map = new WeakMap();
/* 遍历二叉树,存映射关系 */
while (queue.length) {
let size = queue.length;
while (size--) {
let front = queue.shift();/* 队首 */
if (front.left) {
queue.push(front.left);
map.set(front.left, front);
}
if (front.right) {
queue.push(front.right);
map.set(front.right, front);
}
}
}
/* 记录p的所有祖先 */
let acts = new Set();
while (p) {
acts.add(p);//放到set中
p = map.get(p);//父结点
}
/* 查找公共祖先 */
while (q) {
if (acts.has(q)) return q;
q = map.get(q);
}
};
2.深度优先搜索解法
原理:根据p,q是否分别在两侧子树判断LCA
(1)深度优先遍历二叉树,如果当前节点为 p
或者 q
,直接返回这个节点,
(2)不符合以上条件,查看左右孩子,左孩子中不包含 p
或q
则去找右孩子,右孩子不包含 p
或者 q
就去找左孩子
(3)左右孩子中都存在 p
或者 q
, 那么这个节点就是LCA(只有对于最近公共祖先p,q才会在不同侧)
var lowestCommonAncestor = function (root, p, q) {
/* root为null,p,q这三种情况时返回 */
if (!root || root === p || root === q) return root;
/* 查找左子树 */
let left = lowestCommonAncestor(root.left, p, q);
/* 查找右子树 */
let right = lowestCommonAncestor(root.right, p, q);
/* 左子树不存在p/q */
if (!left) return right;
else if (!right) return left;
else return root;//左右子树各存在p,q中的一个,为公共祖先
};
补充
对于二叉搜索树,还可以进行优化,通过权值大小比对可以确定p,q相对root的位置,如果p,q分别在root的两侧,则root就是最近公共祖先。
题目链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/submissions/
递归法:
var lowestCommonAncestor = function (root, p, q) {
if (!root || root === p || root === q) return root;
/* 判断p,q在root的哪边,如果一左一右则root就是最近公共祖先 */
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;
}
};
非递归:
var lowestCommonAncestor = function (root, p, q) {
if (!root || root === p || root === q) return root;
while (root) {
if (root.val > p.val && root.val > q.val) {
root = root.left;
} else if (root.val < p.val && root.val < q.val) {
root = root.right;
} else {
return root;
}
}
};