Source
Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two nodes. The lowest common ancestor is the node with largest depth which is the ancestor of both nodes. Example 4 / \ 3 7 / \ 5 6 For 3 and 5, the LCA is 4. For 5 and 6, the LCA is 7. For 6 and 7, the LCA is 7.
題解1 - 自底向上
初次接觸這種題可能會沒有什麼思路,在沒有思路的情況下我們就從簡單例子開始分析!首先看看3和5,這兩個節點分居根節點4的兩側,如果可以從子節點往父節點遞推,那麼他們將在根節點4處第一次重合;再來看看5和6,這兩個都在根節點4的右側,沿著父節點往上遞推,他們將在節點7處第一次重合;最後來看看6和7,此時由於7是6的父節點,故7即為所求。從這三個基本例子我們可以總結出兩種思路——自頂向下(從前往後遞推)和自底向上(從後往前遞推)。
順著上述例項的分析,我們首先看看自底向上的思路,自底向上的實現用一句話來總結就是——如果遍歷到的當前節點是 A/B 中的任意一個,那麼我們就向父節點彙報此節點,否則遞迴到節點為空時返回空值。具體來說會有如下幾種情況:
1、當前節點不是兩個節點中的任意一個,此時應判斷左右子樹的返回結果。
- 若左右子樹均返回非空節點,那麼當前節點一定是所求的根節點,將當前節點逐層向前彙報。// 兩個節點分居樹的兩側
- 若左右子樹僅有一個子樹返回非空節點,則將此非空節點向父節點彙報。// 節點僅存在於樹的一側
- 若左右子樹均返回NULL, 則向父節點返回NULL. // 節點不在這棵樹中
2、當前節點即為兩個節點中的一個,此時向父節點返回當前節點。
根據此遞迴模型容易看出應該使用先序/後序遍歷來實現。
C++ Recursion From Bottom to Top
/** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * TreeNode(int val) { * this->val = val; * this->left = this->right = NULL; * } * } */ class Solution { public: /** * @param root: The root of the binary search tree. * @param A and B: two nodes in a Binary. * @return: Return the least common ancestor(LCA) of the two nodes. */ TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *A, TreeNode *B) { // return either A or B or NULL if (NULL == root || root == A || root == B) return root; TreeNode *left = lowestCommonAncestor(root->left, A, B); TreeNode *right = lowestCommonAncestor(root->right, A, B); // A and B are on both sides if ((NULL != left) && (NULL != right)) return root; // either left or right or NULL return (NULL != left) ? left : right; } };
Java
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */ public class Solution { /** * @param root: The root of the binary search tree. * @param A and B: two nodes in a Binary. * @return: Return the least common ancestor(LCA) of the two nodes. */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { if (root == null) return null; TreeNode lNode = lowestCommonAncestor(root.left, A, B); TreeNode rNode = lowestCommonAncestor(root.right, A, B); // root is the LCA of A and B if (lNode != null && rNode != null) return root; // root node is A/B(including the case below) if (root == A || root == B) return root; // return lNode/rNode if root is not LCA return (lNode != null) ? lNode : rNode; } }
原始碼分析
結合例子和遞迴的整體思想去理解程式碼,在root == A || root == B後即層層上浮(自底向上),直至找到最終的最小公共祖先節點。
最後一行return (NULL != left) ? left : right;將非空的左右子樹節點和空值都包含在內了。
關於重複節點:由於這裡比較的是元素地址,因此可以認為樹中不存在重複元素,否則不符合樹的資料結構。
題解 - 自底向上(計數器)
為了解決上述方法可能導致誤判的情況,我們可以對返回結果新增計數器來解決。由於此計數器的值只能由子樹向上遞推,故應該用後序遍歷。在類中新增私有變數較為方便。
定義pair<TreeNode *, int> result(node, counter)表示遍歷到某節點時的返回結果,返回的node表示LCA 路徑中的可能的最小節點,相應的計數器counter則表示目前和A或者B匹配的節點數,若計數器為2,則表示已匹配過兩次,該節點即為所求,若只匹配過一次,還需進一步向上遞推。表述地可能比較模糊,還是看看程式碼吧。
C++
/** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * TreeNode(int val) { * this->val = val; * this->left = this->right = NULL; * } * } */ class Solution { public: /** * @param root: The root of the binary search tree. * @param A and B: two nodes in a Binary. * @return: Return the least common ancestor(LCA) of the two nodes. */ TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *A, TreeNode *B) { if ((NULL == A) || (NULL == B)) return NULL; pair<TreeNode *, int> result = helper(root, A, B); if (A != B) { return (2 == result.second) ? result.first : NULL; } else { return (1 == result.second) ? result.first : NULL; } } private: pair<TreeNode *, int> helper(TreeNode *root, TreeNode *A, TreeNode *B) { TreeNode * node = NULL; if (NULL == root) return make_pair(node, 0); pair<TreeNode *, int> left = helper(root->left, A, B); pair<TreeNode *, int> right = helper(root->right, A, B); // return either A or B int count = max(left.second, right.second); if (A == root || B == root) return make_pair(root, ++count); // A and B are on both sides if (NULL != left.first && NULL != right.first) return make_pair(root, 2); // return either left or right or NULL return (NULL != left.first) ? left : right; } };
Java
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */ public class Solution { private int count = 0; /** * @param root: The root of the binary search tree. * @param A and B: two nodes in a Binary. * @return: Return the least common ancestor(LCA) of the two nodes. */ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { TreeNode result = helper(root, A, B); if (A == B) { return result; } else { return (count == 2) ? result : null; } } private TreeNode helper(TreeNode root, TreeNode A, TreeNode B) { if (root == null) return null; TreeNode lNode = helper(root.left, A, B); TreeNode rNode = helper(root.right, A, B); // root is the LCA of A and B if (lNode != null && rNode != null) return root; // root node is A/B(including the case below) if (root == A || root == B) { count++; return root; } // return lNode/rNode if root is not LCA return (lNode != null) ? lNode : rNode; } }
原始碼分析