【IT笔试面试题整理】寻找二叉树两节点的最近的公共祖先

时间:2023-03-09 08:45:33
【IT笔试面试题整理】寻找二叉树两节点的最近的公共祖先

【试题描述】

求二叉树中任意两个节点的最近公共祖先也称为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