最近公共祖先 python_查找最近公共祖先(LCA)

一、问题

求有根树的任意两个节点的最近公共祖先(一般来说都是指二叉树)。最近公共祖先简称LCA(Lowest Common Ancestor)。例如,如下图一棵普通的二叉树。

结点3和结点4的最近公共祖先是结点2,即LCA(3,4)=2 。在此,需要注意到当两个结点在同一棵子树上的情况,如结点3和结点2的最近公共祖先为2,即 LCA(3,2)=2。同理:LCA(5,6)=4,LCA(6,10)=1。

明确了题意,咱们便来试着解决这个问题。直观的做法,可能是针对是否为二叉查找树分情况讨论,这也是一般人最先想到的思路。除此之外,还有所谓的Tarjan算法、倍增算法、以及转换为RMQ问题(求某段区间的极值)。后面这几种算法相对高级,不那么直观,但思路比较有启发性,留作以后了解一下也有裨益。

二、思路

解法一:暴力解法,在有parent指针的情况下,对两个节点依次向上回溯,直到两个节点相同,那么这个节点就是最近公共祖先。时间复杂度为O(n²)

解法二:链表的交叉,在有parent指针的情况下,对两个节点分别到根节点的路径上的节点形成两个链表,因为两个链表很大可能不一样长,然后我们可以对其中长的一个链表从开头进行裁剪,形成两个链表长度一样,然后遍历直到相等,虽说优化了一点,但本质上还是暴力破解。

解法三:借用数组和列表,在没有parent指针的情况,我们只能从根节点往下遍历,而不能进行往上回溯。所以可以借用数组或列表来保存数据,后面进行比对。和链表的交叉差不多。

解法四:不借用额外的数据结构,没有parent指针。大概思路呢就是如果两个节点分属在根节点的两边,返回根节点,如果两个节点同在左子树或右子树,递归求解。

解法五:这种解法现在不太懂,现在留作记录以后观看。其中解法四和解法五在代码中体现。

三、代码

1 import java.util.ArrayList;

2 import java.util.List;

3

4 public class LCA {

5 public int getLCA(int a, int b) {

6 TreeNode root = of(10);

7

8 TreeNode lca = getLCA2(root, new TreeNode(a), new TreeNode(b));

9 return lca == null ? -1 : lca.val;

10 }

11

12 //=====解法四===========

13 // 看两个节点是否在同一侧

14 private TreeNode getLCA(TreeNode root, TreeNode p, TreeNode q) {

15 if (root == null)

16 return null;

17 if (root.equals(p) || root.equals(q))

18 return root;

19

20 boolean is_p_on_left = cover(root.left, p);

21 boolean is_q_on_right = cover(root.right, q);

22 if (is_p_on_left == is_q_on_right) {// 在root的两端

23 return root;

24 } else if (is_p_on_left) {// 在root的左端

25 return getLCA(root.left, p, q);

26 } else {

27 return getLCA(root.right, p, q);

28 }

29 }

30

31 // 解法五

32 // 很难理解 递归定义不明确 第一次看到

33 private TreeNode getLCA2(TreeNode root, TreeNode p, TreeNode q) {

34 if (root == null)

35 return null;

36 if (root.equals(p) && root.equals(q))

37 return root;

38

39 // x是lca,或者是p(p在这一侧),或者是q(q在这一侧),或者是null(pq都不在这一侧)

40 TreeNode x = getLCA2(root.left, p, q);

41 if (x != null && !x.equals(p) && !x.equals(q)) {// 在左子树找到了lca

42 return x;

43 }

44

45 TreeNode y = getLCA2(root.right, p, q);

46 if (y != null && !y.equals(p) && !y.equals(q)) {// 在右子树找到了lca

47 return y;

48 }

49

50 // x:p,q,null y :q,p,null

51 if (x != null && y != null) {// 一边找着一个

52 return root;

53 } else if (root.equals(p) || root.equals(q)) {

54 return root;

55 } else {

56 return x == null ? y : x;// 有一个不为null,则返回,都为null,返回null

57 }

58 }

59

60 /**

61 * 判断x节点是否在n所代表的子树中

62 *

63 * @param n

64 * @param x

65 * @return

66 */

67 private boolean cover(TreeNode n, TreeNode x) {

68 if (n == null)

69 return false;

70 if (n.equals(x))

71 return true;

72 return cover(n.left, x) || cover(n.right, x);

73 }

74

75 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {

76 // 发现目标节点则通过返回值标记该子树发现了某个目标结点

77 if (root == null || root.equals(p.val) || root.equals(q))

78 return root;

79 // 查看左子树中是否有目标结点,没有为null

80 TreeNode left = lowestCommonAncestor(root.left, p, q);

81 // 查看右子树是否有目标节点,没有为null

82 TreeNode right = lowestCommonAncestor(root.right, p, q);

83 // 都不为空,说明左右子树都有目标结点,则公共祖先就是本身

84 if (left != null && right != null)

85 return root;

86 // 如果发现了目标节点,则继续向上标记为该目标节点

87 return left == null ? right : left;

88 }

89

90 static TreeNode of(int n) {

91 List> list = new ArrayList>();

92 for (int i = 0; i < n; i++) {

93 list.add(new TreeNode(i + 1));

94 }

95 for (int i = 0; i < n; i++) {

96 TreeNode parent = list.get(i);

97 if (i * 2 + 1 < n) {

98 TreeNode left = list.get(i * 2 + 1);

99 parent.left = left;

100 left.parent = parent;

101 } else

102 break;

103 if (i * 2 + 2 < n) {

104 TreeNode right = list.get(i * 2 + 2);

105 parent.right = right;

106 right.parent = parent;

107 }

108 }

109 return list.get(0);

110 }

111

112 private static class TreeNode {

113 public T val;

114 public TreeNode left = null;

115 public TreeNode right = null;

116 TreeNode parent;

117

118 public TreeNode(T val) {

119 this.val = val;

120 }

121

122 @Override

123 public boolean equals(Object o) {

124 if (this == o)

125 return true;

126 if (o == null || getClass() != o.getClass())

127 return false;

128

129 TreeNode> treeNode = (TreeNode>) o;

130

131 return val != null ? val.equals(treeNode.val) : treeNode.val == null;

132 }

133

134 @Override

135 public int hashCode() {

136 return val != null ? val.hashCode() : 0;

137 }

138 }

139 }

作者:|旧市拾荒|

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值