Using a bottom-up approach, we can improve over the top-down approach by avoiding traversing the same nodes over and over again.
We traverse from the bottom, and once we reach a node which matches one of the two nodes, we pass it up to its parent. The parent would then test its left and right subtree if each contain one of the two nodes. If yes, then the parent must be the LCA and we pass its parent up to the root. If not, we pass the lower node which contains either one of the two nodes (if the left or right subtree contains either p or q), or NULL (if both the left and right subtree does not contain either p or q) up.
Sounds complicated? Surprisingly the code appears to be much simpler than the top-down one.
from : http://articles.leetcode.com/2011/07/lowest-common-ancestor-of-a-binary-tree-part-i.html
classSolution(object): deflowestCommonAncestor(self, root, p, q): """ :type root: TreeNode :type p: TreeNode :type q: TreeNode :rtype: TreeNode """ ifnot root or root == p or root == q: return root left = self.lowestCommonAncestor(root.left, p, q) right = self.lowestCommonAncestor(root.right, p, q) if left and right: return root return left if left else right
publicclassTreeNode { int val; TreeNode left; TreeNode right; TreeNode parent; TreeNode(int x) { val = x; } }
那么,一个简单的思路,对p和q向上走,用hashtable记录访问过的节点,
如果某个节点已经被访问过了,那么返回该节点。
复杂度O(h),h为树的高度
1 2 3 4 5 6 7 8 9 10 11 12 13 14
defLCA(root, p, q): vis = set() while p or q: if p: if p in vis: return p vis.add(p) p = p.parent if q: if q in vis: return q vis.add(q) q = q.parent returnNone
intgetHeight(Node *p){ int height = 0; while (p) { height++; p = p->parent; } return height; } // As root->parent is NULL, we don't need to pass root in. Node *LCA(Node *p, Node *q){ int h1 = getHeight(p); int h2 = getHeight(q); // swap both nodes in case p is deeper than q. if (h1 > h2) { swap(h1, h2); swap(p, q); } // invariant: h1 <= h2. int dh = h2 - h1; for (int h = 0; h < dh; h++) q = q->parent; while (p && q) { if (p == q) return p; p = p->parent; q = q->parent; } returnNULL; // p and q are not in the same tree }
多次查询的普通二叉树
如果是多次查询呢?每次都递归一次?
这样有q次查询就要O(q * n) !
显然这复杂度太高。
我们可以用Tarjan离线算法来求LCA
Tarjan算法基于DFS和并查集进行求解。
我们知道,树的后序遍历顺序是:左子树,右子树,根节点。
所以,如果LCA(p,q) = x ,那么,进行后续遍历的时候,必然先访问完x的左右子树,才会返回x所在的节点。