原文:
Design an algorithm and write code to find the first common ancestor of two nodes in a binary tree. Avoid storing additional nodes in a data structure. NOTE: This is not necessarily a binary search tree.
译文:
写程序在一棵二叉树中找到两个结点的第一个共同祖先。不允许存储额外的结点。注意: 这里不特指二叉查找树。
思路:
其实没有什么难得,入手点就是分情况,搞清楚p,q能在树的位置的各种情况。
我们可以从根结点开始,判断以当前结点为根的树中左右子树是不是包含我们要找的两个结点。如果两个结点都出现在它的左子树中,那最低的共同父结点也出现在它的左子树中。如果两个结点都出现在它的右子树中,那最低的共同父结点也出现在它的右子树中。如果两个结点一个出现在左子树中,一个出现在右子树中,那当前的结点就是最低的共同父结点
package Tree_Graph;
import CtCILibrary.TreeNode;
public class S4_7 {
// ========================= 未优化递归版
// 递归求公共祖先,Time:O(N)
public static TreeNode commonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 错误判断,如果有一个点根本不在树内,则报错!
if(!isIn(root.left, p) || !isIn(root.right, q)) {
return null;
}
return commonAncestorRec(root, p, q);
}
public static TreeNode commonAncestorRec(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
if(root==p || root==q) {
return root;
}
boolean isPInLeft = isIn(root.left, p); // 判断p是否在树的左侧
boolean isQInLeft = isIn(root.left, q); // 判断q是否在树的左侧
if(isPInLeft != isQInLeft){ // 如果一个在左侧一个在右侧,则公共祖先必然是root
return root;
}
if(isPInLeft) { // p,q都在左侧
return commonAncestorRec(root.left, p, q);
} else{ // p,q都在右侧
return commonAncestorRec(root.right, p, q);
}
}
// 判断节点p是否在root树中
private static boolean isIn(TreeNode root, TreeNode node) {
if(root == null) {
return false;
}
if(root == node) {
return true;
}
return isIn(root.left, node) || isIn(root.right, node);
}
// ========================= 优化递归版,待改动
public static TreeNode commonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
Result res = commonAncestorRec2(root, p, q);
if(res.isAncestor) {
return res.node;
}
return null;
}
public static Result commonAncestorRec2(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return new Result(null, false);
}
if(root==p && root==q) {
return new Result(root, true);
}
Result leftRes = commonAncestorRec2(root.left, p, q);
if(leftRes.isAncestor) { // 在左子树找到公共节点
return leftRes;
}
Result rightRes = commonAncestorRec2(root.right, p, q);
if(rightRes.isAncestor) { // 在右子树找到公共节点
return rightRes;
}
if(leftRes.node!=null && rightRes.node!=null) {
return new Result(root, true); // root为公共节点
}
else if(root==p || root==q) {
boolean isAncestor = (leftRes.node != null || rightRes.node != null) ? true : false;
return new Result(root, isAncestor);
}
else {
return new Result(leftRes!=null ? leftRes.node : rightRes.node, false);
}
}
static class Result {
public TreeNode node;
public boolean isAncestor;
public Result(TreeNode n, boolean isAnc) {
node = n;
isAncestor = isAnc;
}
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
TreeNode root = TreeNode.createMinimalBST(array);
TreeNode n3 = root.find(1);
TreeNode n7 = root.find(7);
TreeNode ancestor = commonAncestor(root, n3, n7);
System.out.println(ancestor.data);
}
}
Follow Up:
假如这棵树是BST,那么怎么做?
就不必用isIn函数了,直接把待判断节点的值与root相比较即可,下面写了递归和非递归版解法:
public class TreeNode {
int data;
TreeNode left, right;
}
// 递归 Time Complexity: O(h) h: 树高
// Space complexity: O(n) n:节点个数
public TreeNode lca(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
if(p.data < root.data && q.data < root.data) {
return lca(root.left, p, q);
}
if(p.data > root.data && q.data > root.data) {
return lca(root.right, p, q);
}
return root;
}
// Iteration不用递归
// Time Complexity: O(h) h: 树高
// Space complexity: O(1)
public TreeNode lca2(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
while(root != null) {
if(p.data < root.data && q.data < root.data) { // p,q都在左子树
return lca(root.left, p, q);
}
else if(p.data > root.data && q.data > root.data) { // p,q都在右子树
return lca(root.right, p, q);
}
else { // p,q分别在不同子树
break;
}
}
return root;
}
Ref:
http://zhedahht.blog.163.com/blog/static/25411174201081263815813/
http://hawstein.com/posts/4.6.html
http://www.geeksforgeeks.org/lowest-common-ancestor-in-a-binary-search-tree/