1. import java.util.ArrayList;
    2. import java.util.HashMap;
    3. import java.util.HashSet;
    4. /**
    5. * 题目:
    6. * 给定一棵二叉树的头节点head,和另外两个节点a和b,返回a和b的最低公共祖先
    7. * 规定:如果b的父节点是a,则ab的公共祖先为a。
    8. */
    9. public class Code03_lowestAncestor {
    10. public static class Node {
    11. public int value;
    12. public Node left;
    13. public Node right;
    14. public Node(int data) {
    15. this.value = data;
    16. }
    17. }
    18. // 方式1:通过二叉树的依次遍历,通过map来记录每个节点的父亲节点。key存节点,value存key节点的父节点
    19. /**
    20. * map构建完成之后,通过map找到a的所有父节点,并将这些父节点保存在集合set中。
    21. * 然后依次找节点b的父节点,一旦发现该节点出现在集合set中,则该节点就是ab的公共祖先节点。
    22. */
    23. public static Node lowestAncestor1(Node head, Node o1, Node o2) {
    24. if (head == null) {
    25. return null;
    26. }
    27. // key的父节点是value
    28. HashMap<Node, Node> parentMap = new HashMap<>();
    29. parentMap.put(head, null);
    30. fillParentMap(head, parentMap);
    31. // 寻找一个节点的所有父节点保存set集合中
    32. HashSet<Node> o1Set = new HashSet<>();
    33. Node cur = o1;
    34. o1Set.add(cur);
    35. while (parentMap.get(cur) != null) {
    36. cur = parentMap.get(cur);
    37. o1Set.add(cur);
    38. }
    39. // 依次寻找另外一个节点的父节点,和set集合比较,如果存在set中则就是该节点
    40. cur = o2;
    41. while (!o1Set.contains(cur)) {
    42. cur = parentMap.get(cur);
    43. }
    44. return cur;
    45. }
    46. /**
    47. * 二叉树遍历,采用map保存节点的父节点信息。p
    48. */
    49. public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
    50. if (head.left != null) {
    51. parentMap.put(head.left, head);
    52. fillParentMap(head.left, parentMap);
    53. }
    54. if (head.right != null) {
    55. parentMap.put(head.right, head);
    56. fillParentMap(head.right, parentMap);
    57. }
    58. }
    59. /**
    60. * 方式二:采用二叉树的递归套路
    61. *
    62. * ①目标:给定节点,a和b,求X为头节点的这棵树上,a和b最初汇聚的节点为什么?如果有汇聚节点返回该节点,如果没有汇聚,返回空
    63. *
    64. * ②分析可能性:
    65. * 首先考虑这棵树上有没有发现a,b节点,如果没有发现a和b则肯定不汇聚
    66. *
    67. * 第一类:汇聚点和X无关,X不是最低的汇聚点。
    68. * 1. 汇聚点在左树上
    69. * 2. 汇聚点在右树上
    70. * 3. X的整棵树上,a和b没有找全
    71. *
    72. * 第二类: 汇聚点和X有关,X是最低的汇聚点
    73. * 1. 左a,右b,在X汇聚
    74. * 2. 左b,右a,在X汇聚
    75. * 3. 左或者右有a, x = b
    76. * 4. 左或者右有b, x = a
    77. * 如果上述可能性都没中,则整棵树上没有答案。
    78. *
    79. * ③ 整合Info:
    80. * 是否有a
    81. * 是否有b
    82. * 树中是否发现a,b最低公共祖先 an
    83. *
    84. */
    85. public static Node lowestAncestor2(Node head, Node a, Node b) {
    86. return process(head, a, b).ans;
    87. }
    88. public static class Info {
    89. public boolean findA;
    90. public boolean findB;
    91. public Node ans; // 最低公共祖先的值
    92. public Info(boolean fA, boolean fB, Node an) {
    93. findA = fA;
    94. findB = fB;
    95. ans = an;
    96. }
    97. }
    98. public static Info process(Node x, Node a, Node b) {
    99. if (x == null) {
    100. return new Info(false, false, null);
    101. }
    102. // 获取左右子树信息
    103. Info leftInfo = process(x.left, a, b);
    104. Info rightInfo = process(x.right, a, b);
    105. // 整合自己的info
    106. boolean findA = (x == a) || leftInfo.findA || rightInfo.findA; // x为a,或者左树发现a,或者右树发现a。则这棵树上就发现a为真
    107. boolean findB = (x == b) || leftInfo.findB || rightInfo.findB;
    108. Node ans = null;
    109. if (leftInfo.ans != null) { // 左树有答案
    110. ans = leftInfo.ans;
    111. } else if (rightInfo.ans != null) { // 右树有答案
    112. ans = rightInfo.ans;
    113. } else { // 左右子树都没有答案,并且这棵树上有a和b,这时候x就是最低汇聚点
    114. if (findA && findB) {
    115. ans = x;
    116. }
    117. }
    118. return new Info(findA, findB, ans);
    119. }
    120. // for test
    121. public static Node generateRandomBST(int maxLevel, int maxValue) {
    122. return generate(1, maxLevel, maxValue);
    123. }
    124. // for test
    125. public static Node generate(int level, int maxLevel, int maxValue) {
    126. if (level > maxLevel || Math.random() < 0.5) {
    127. return null;
    128. }
    129. Node head = new Node((int) (Math.random() * maxValue));
    130. head.left = generate(level + 1, maxLevel, maxValue);
    131. head.right = generate(level + 1, maxLevel, maxValue);
    132. return head;
    133. }
    134. // for test
    135. public static Node pickRandomOne(Node head) {
    136. if (head == null) {
    137. return null;
    138. }
    139. ArrayList<Node> arr = new ArrayList<>();
    140. fillPrelist(head, arr);
    141. int randomIndex = (int) (Math.random() * arr.size());
    142. return arr.get(randomIndex);
    143. }
    144. // for test
    145. public static void fillPrelist(Node head, ArrayList<Node> arr) {
    146. if (head == null) {
    147. return;
    148. }
    149. arr.add(head);
    150. fillPrelist(head.left, arr);
    151. fillPrelist(head.right, arr);
    152. }
    153. public static void main(String[] args) {
    154. int maxLevel = 4;
    155. int maxValue = 100;
    156. int testTimes = 1000000;
    157. for (int i = 0; i < testTimes; i++) {
    158. Node head = generateRandomBST(maxLevel, maxValue);
    159. Node o1 = pickRandomOne(head);
    160. Node o2 = pickRandomOne(head);
    161. if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) {
    162. System.out.println("Oops!");
    163. }
    164. }
    165. System.out.println("finish!");
    166. }
    167. }