【试题描述】
求二叉树中任意两个节点的最近公共祖先也称为LCA问题(Lowest Common Ancestor)。
二叉查找树
如果该二叉树是二叉查找树,那么求解LCA十分简单。
基本思想为:从树根开始,该节点的值为t,如果t大于t1和t2,说明t1和t2都位于t的左侧,所以它们的共同祖先必定在t的左子树中,从t.left开始搜索;如果t小于t1和t2,说明t1和t2都位于t的右侧,那么从t.right开始搜索;如果t1<t< t2,说明t1和t2位于t的两侧,那么该节点t为公共祖先。
如果t1是t2的祖先,那么应该返回t1的父节点;同理,如果t2是t1的祖先,应该返回t2的父节点。
【参考代码】
1 public int query(Node t1, Node t2, Node t) { 2 int left = t1.value; 3 int right = t2.value; 4 Node parent = null; 5 6 if (left > right) { 7 int temp = left; 8 left = right; 9 right = temp; 10 } 11 12 while (true) { 13 if (t.value < left) { 14 parent = t; 15 t = t.right; 16 } else if (t.value > right) { 17 parent = t; 18 t = t.left; 19 } else if (t.value == left || t.value == right) { 20 return parent.value; 21 } else { 22 return t.value; 23 } 24 } 25 }
普通二叉树
算法思想:如果一个节点的左子树包含p,q中的一个节点,右子树包含另一个,则这个节点就是p,q的最近公共祖先。
【参考代码】
解法一:
1 public static Node findNCA(Node root, Node p, Node q) 2 { 3 if (isintree(root.left, p) && isintree(root.left, q)) 4 return findNCA(root.left, p, q); 5 if (isintree(root.right, p) && isintree(root.right, q)) 6 return findNCA(root.right, p, q); 7 return root; 8 } 9 10 public static boolean isintree(Node root, Node node) 11 { 12 if (root == null) 13 return false; 14 if (root == node) 15 return true; 16 return isintree(root.left, node) || isintree(root.right, node); 17 }
解法二:
1 static int TWO_NODES_FOUND = 2; 2 static int ONE_NODES_FOUND = 1; 3 static int NO_NODES_FOUND = 0; 4 5 public static int covers(Node root, Node p, Node q) 6 { 7 int ret = NO_NODES_FOUND; 8 if (root == null) 9 return ret; 10 if (root == p || root == q) 11 ret += 1; 12 ret += covers(root.left, p, q); 13 if (ret == TWO_NODES_FOUND) 14 return ret; 15 return ret + covers(root.right, p, q); 16 } 17 18 private static Node findNCA(Node root, Node p, Node q) 19 { 20 if (q == p && (root.left == q || root.right == q)) 21 return root; 22 int nodesFromLeft = covers(root.left, p, q); 23 if (nodesFromLeft == TWO_NODES_FOUND) 24 { 25 if (root.left == p || root.left == q) 26 return root.left; 27 else 28 return findNCA(root.left, p, q); 29 } else if (nodesFromLeft == ONE_NODES_FOUND) 30 { 31 if (root == p) 32 return p; 33 else if (root == q) 34 return q; 35 } 36 37 int nodesFromRight = covers(root.right, p, q); 38 if (nodesFromRight == TWO_NODES_FOUND) 39 { 40 if (root.right == p || root.right == q) 41 return root.right; 42 else 43 return findNCA(root.right, p, q); 44 } else if (nodesFromRight == ONE_NODES_FOUND) 45 { 46 if (root == p) 47 return p; 48 else if (root == q) 49 return q; 50 } 51 52 if (nodesFromLeft == ONE_NODES_FOUND 53 && nodesFromLeft == ONE_NODES_FOUND) 54 return root; 55 else 56 return null; 57 }
解法三:
网上版本:
1 public static int FindNCA(Node root, Node a, Node b, Node out) 2 { 3 if (root == null) 4 return 0; 5 if (root == a || root == b) 6 return 1; 7 8 int iLeft = FindNCA(root.left, a, b, out); 9 if (iLeft == 2) 10 return 2; 11 12 int iRight = FindNCA(root.right, a, b, out); 13 if (iRight == 2) 14 return 2; 15 16 if (iLeft + iRight == 2) 17 out = root; 18 19 return iLeft + iRight; 20 }
这个网上说输出时 当为2时才输出,但是为2时,不能判断如果其中一个是另一个的父亲节点情况。所以理论上应该改为当返回结果
大于0时,就可以输出结果。但是不太确定正确性,应该程序是正确的。
参考:
http://blog.csdn.net/w397090770/article/details/7615447