import java.util.ArrayList;import java.util.HashMap;import java.util.HashSet;/** * 题目: * 给定一棵二叉树的头节点head,和另外两个节点a和b,返回a和b的最低公共祖先 * 规定:如果b的父节点是a,则ab的公共祖先为a。 */public class Code03_lowestAncestor { public static class Node { public int value; public Node left; public Node right; public Node(int data) { this.value = data; } } // 方式1:通过二叉树的依次遍历,通过map来记录每个节点的父亲节点。key存节点,value存key节点的父节点 /** * map构建完成之后,通过map找到a的所有父节点,并将这些父节点保存在集合set中。 * 然后依次找节点b的父节点,一旦发现该节点出现在集合set中,则该节点就是ab的公共祖先节点。 */ public static Node lowestAncestor1(Node head, Node o1, Node o2) { if (head == null) { return null; } // key的父节点是value HashMap<Node, Node> parentMap = new HashMap<>(); parentMap.put(head, null); fillParentMap(head, parentMap); // 寻找一个节点的所有父节点保存set集合中 HashSet<Node> o1Set = new HashSet<>(); Node cur = o1; o1Set.add(cur); while (parentMap.get(cur) != null) { cur = parentMap.get(cur); o1Set.add(cur); } // 依次寻找另外一个节点的父节点,和set集合比较,如果存在set中则就是该节点 cur = o2; while (!o1Set.contains(cur)) { cur = parentMap.get(cur); } return cur; } /** * 二叉树遍历,采用map保存节点的父节点信息。p */ public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) { if (head.left != null) { parentMap.put(head.left, head); fillParentMap(head.left, parentMap); } if (head.right != null) { parentMap.put(head.right, head); fillParentMap(head.right, parentMap); } } /** * 方式二:采用二叉树的递归套路 * * ①目标:给定节点,a和b,求X为头节点的这棵树上,a和b最初汇聚的节点为什么?如果有汇聚节点返回该节点,如果没有汇聚,返回空 * * ②分析可能性: * 首先考虑这棵树上有没有发现a,b节点,如果没有发现a和b则肯定不汇聚 * * 第一类:汇聚点和X无关,X不是最低的汇聚点。 * 1. 汇聚点在左树上 * 2. 汇聚点在右树上 * 3. X的整棵树上,a和b没有找全 * * 第二类: 汇聚点和X有关,X是最低的汇聚点 * 1. 左a,右b,在X汇聚 * 2. 左b,右a,在X汇聚 * 3. 左或者右有a, x = b * 4. 左或者右有b, x = a * 如果上述可能性都没中,则整棵树上没有答案。 * * ③ 整合Info: * 是否有a * 是否有b * 树中是否发现a,b最低公共祖先 an * */ public static Node lowestAncestor2(Node head, Node a, Node b) { return process(head, a, b).ans; } public static class Info { public boolean findA; public boolean findB; public Node ans; // 最低公共祖先的值 public Info(boolean fA, boolean fB, Node an) { findA = fA; findB = fB; ans = an; } } public static Info process(Node x, Node a, Node b) { if (x == null) { return new Info(false, false, null); } // 获取左右子树信息 Info leftInfo = process(x.left, a, b); Info rightInfo = process(x.right, a, b); // 整合自己的info boolean findA = (x == a) || leftInfo.findA || rightInfo.findA; // x为a,或者左树发现a,或者右树发现a。则这棵树上就发现a为真 boolean findB = (x == b) || leftInfo.findB || rightInfo.findB; Node ans = null; if (leftInfo.ans != null) { // 左树有答案 ans = leftInfo.ans; } else if (rightInfo.ans != null) { // 右树有答案 ans = rightInfo.ans; } else { // 左右子树都没有答案,并且这棵树上有a和b,这时候x就是最低汇聚点 if (findA && findB) { ans = x; } } return new Info(findA, findB, ans); } // for test public static Node generateRandomBST(int maxLevel, int maxValue) { return generate(1, maxLevel, maxValue); } // for test public static Node generate(int level, int maxLevel, int maxValue) { if (level > maxLevel || Math.random() < 0.5) { return null; } Node head = new Node((int) (Math.random() * maxValue)); head.left = generate(level + 1, maxLevel, maxValue); head.right = generate(level + 1, maxLevel, maxValue); return head; } // for test public static Node pickRandomOne(Node head) { if (head == null) { return null; } ArrayList<Node> arr = new ArrayList<>(); fillPrelist(head, arr); int randomIndex = (int) (Math.random() * arr.size()); return arr.get(randomIndex); } // for test public static void fillPrelist(Node head, ArrayList<Node> arr) { if (head == null) { return; } arr.add(head); fillPrelist(head.left, arr); fillPrelist(head.right, arr); } public static void main(String[] args) { int maxLevel = 4; int maxValue = 100; int testTimes = 1000000; for (int i = 0; i < testTimes; i++) { Node head = generateRandomBST(maxLevel, maxValue); Node o1 = pickRandomOne(head); Node o2 = pickRandomOne(head); if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) { System.out.println("Oops!"); } } System.out.println("finish!"); }}