二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 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;
}
}
};