1. 二叉树

2. 递归序

先序遍历 : 头节点 左节点 右节点

中序遍历 : 左节点 头节点 右节点

后序遍历 : 左节点 右节点 头节点

  1. public static class Node {
  2. public int val;
  3. public Node left;
  4. public Node right;
  5. public Node(int val) {
  6. this.val = val;
  7. }
  8. }
  9. public static void f(Node head) {
  10. if(head == null) {
  11. return;
  12. }
  13. System.out.println("先序遍历"+head.val); //头节点 左节点 右节点
  14. f(head.left);
  15. // System.out.println("中序遍历"+head.val); //左节点 头节点 右节点
  16. f(head.right);
  17. // System.out.println("后序遍历"+head.val); //左节点 右节点 头节点
  18. }
  19. public static void main(String[] args) {
  20. Node head = new Node(1);
  21. head.left = new Node(2);
  22. head.right = new Node(3);
  23. head.left.left = new Node(4);
  24. head.left.right = new Node(5);
  25. head.right.left = new Node(6);
  26. head.right.right = new Node(8);
  27. f(head);
  28. }

3. 非递归版遍历二叉树顺序

使用Stack类 手动压栈出栈

3.1. 先序

  1. push头节点到栈顶 再pop弹出并输出
  2. 先push右节点 到栈中 压为栈底
  3. 再push左节点 到栈中 压为栈顶 重复上述步骤
  1. //先序遍历 头 左 右
  2. public static void pre(Node head) {
  3. System.out.println("先序遍历");
  4. if(head != null) {
  5. Stack<Node> stack = new Stack<>();
  6. stack.add(head);
  7. while(!stack.isEmpty()) {
  8. head = stack.pop(); //删除栈顶
  9. System.out.println(head.value);
  10. //先push右树 压入栈底
  11. if(head.right != null) {
  12. stack.push(head.right);
  13. }
  14. //再push左树 压入栈顶
  15. if(head.left != null) {
  16. stack.push(head.left);
  17. }
  18. }
  19. }
  20. }

3.2. 2个栈实现后序

上面我们使用1个栈实现了,先序遍历头左右的顺序,我们知道后序遍历为左右头,我们微调一下压入左右树的顺序,实现了头右左的顺序,准备第二个栈按顺序压入,再进行弹出成了左右头的顺序。

  1. //两个栈 实现后序遍历
  2. public static void pos1(Node head) {
  3. System.out.println("后序遍历");
  4. if(head !=null) {
  5. Stack<Node> s1= new Stack<>();
  6. Stack<Node> s2= new Stack<>();
  7. s1.add(head);
  8. while(!s1.isEmpty()) {
  9. head = s1.pop();
  10. s2.push(head);//头节点压入栈 s2栈底
  11. if(head.left != null) {
  12. s1.push(head.left); //左节点 入s1栈底
  13. }
  14. if(head.right != null) { //右节点 入s1栈 此时为栈顶
  15. s1.push(head);
  16. }
  17. //此时s1栈存储为 [右,左]
  18. //s2栈为 [头] 两栈结合为 [左,右,头]
  19. }
  20. // 左 右 头
  21. while(!s2.isEmpty()) {
  22. System.out.println(s2.pop().value);
  23. }
  24. }
  25. }

3.3. 1个栈实现后序

  1. // 一个栈 实现后序遍历
  2. public static void pos2(Node head) {
  3. System.out.println("后序遍历2");
  4. if (head != null) {
  5. Stack<Node> stack = new Stack<>();
  6. stack.push(head);
  7. Node cur = null;
  8. Node pre = null;
  9. while (!stack.isEmpty()) {
  10. cur = stack.peek(); // 只查看栈顶不删除
  11. // 不断将左树入栈 栈顶为左树的叶子节点 pre为了防止之前入过栈的左右节点 重新入栈
  12. if (cur.left != null && pre != cur.left && pre != cur.right) {
  13. stack.push(cur.left);
  14. // 左树无节点/已经入过栈 将右树入栈
  15. } else if (cur.right != null && pre != cur.right) {
  16. stack.push(cur.right);
  17. } else {
  18. // 左右都无节点 直接删除当前栈顶 为头节点
  19. System.out.println(stack.pop().value);
  20. pre = cur;
  21. }
  22. }
  23. }
  24. }

3.4. 中序

  1. // 中序遍历 左 头 右
  2. public static void in(Node cur) {
  3. System.out.println("中序遍历");
  4. if (cur != null) {
  5. Stack<Node> statck = new Stack<>();
  6. while (!statck.isEmpty() || cur != null) {
  7. if (cur != null) {
  8. statck.push(cur);
  9. cur = cur.left; // 遍历根节点 左树叶子节点 入栈
  10. } else {
  11. cur = statck.pop(); // 弹出栈
  12. System.out.println(cur.value);
  13. cur = cur.right;
  14. }
  15. }
  16. }
  17. }

4. 前缀树

5. 二叉树按层变遍历

即宽度优先搜索(BFS) 使用队列实现

  1. //按层遍历二叉树 bfs
  2. public static void levle(Node head) {
  3. if(head ==null) {
  4. return;
  5. }
  6. LinkedList<Node> queue = new LinkedList<>();
  7. queue.add(head);
  8. while(!queue.isEmpty()) {
  9. Node cur = queue.poll();
  10. System.out.println(cur.value);
  11. if(cur.left != null) {
  12. queue.add(head.left);
  13. }
  14. if(cur.right != null) {
  15. queue.add(head.right);
  16. }
  17. }
  18. }

6. 二叉树序列化和反序列化

6.1. 先序遍历序列化

只有先序和后序遍历可以序列化,而中序遍历序列化时会有歧义(即两个节点位置不确定)

  1. // 先序 序列化二叉树
  2. public static Queue<String> preSerial(Node head) {
  3. if (head == null) {
  4. return null;
  5. }
  6. Queue<String> queue = new LinkedList<>();
  7. pres(head, queue);
  8. return queue;
  9. }
  10. private static void pres(Node head, Queue<String> queue) {
  11. if (head == null) {
  12. queue.add(null); // null节点 入栈
  13. } else {
  14. queue.add(String.valueOf(head.value)); // 头节点压栈
  15. pres(head.left, queue);
  16. pres(head.right, queue);
  17. }
  18. }

6.2. 按层遍历序列化

  1. // 先序 反序列化二叉树
  2. public static Node buildByPreQueue(Queue<String> queue) {
  3. if (queue == null || queue.size() == 0) {
  4. return null;
  5. }
  6. return preb(queue);
  7. }
  8. private static Node preb(Queue<String> queue) {
  9. String value = queue.poll();
  10. if (value == null) {
  11. return null;
  12. }
  13. Node head = new Node(Integer.valueOf(value));
  14. head.left = preb(queue);
  15. head.right = preb(queue);
  16. return head;
  17. }

7. 431.Encode N-ary Tree to Binary Tree

设计一种算法,将 N 叉树编码为二叉树,并对二叉树进行解码,得到原始 N 叉树。N-ary 树是一个有根树,其中每个节点不超过 N 个子节点。类似地,二叉树是一个有根树,其中每个节点的子节点不超过 2 个。您的编码/解码算法的工作方式没有限制。您只需要确保可以将 N 叉树编码为二叉树,并且可以将二叉树解码为原始的 N 叉树结构。

例如,您可以通过3-ary这种方式将以下树编码为二叉树:

11. 二叉树 - 图2

请注意,以上只是一个可能会或可能不会起作用的示例。您不一定需要遵循这种格式,因此请发挥创造力并自己提出不同的方法。

思路:

将多叉树的孩子节点 全部挂载左树的右边界

  1. public static class Node {
  2. public int val;
  3. public List<Node> children;
  4. public Node() {
  5. }
  6. public Node(int _val) {
  7. val = _val;
  8. }
  9. public Node(int _val, List<Node> _children) {
  10. val = _val;
  11. children = _children; // 节点下所有孩子(即有多少个这个节点就有多少叉)
  12. }
  13. };
  14. public static class TreeNode {
  15. int val;
  16. TreeNode left;
  17. TreeNode right;
  18. TreeNode(int x) {
  19. val = x;
  20. }
  21. }
  22. class Codec {
  23. // 将多叉树 序列化为二叉树
  24. public TreeNode encode(Node root) {
  25. if (root == null) {
  26. return null;
  27. }
  28. TreeNode head = new TreeNode(root.val);// 先建立 根节点
  29. head.left = en(root.children); // 将孩子节点(多叉树)收为二叉树
  30. return head;
  31. }
  32. // 孩子节点第一个全部挂载左树 剩下的孩子节点挂载在新建左树节点的右树
  33. private TreeNode en(List<Node> children) {
  34. TreeNode head = null;
  35. TreeNode cur = null;
  36. for (Node node : children) {
  37. TreeNode tNode = new TreeNode(node.val); // 当前孩子 新生成节点
  38. if (head == null) {
  39. // 当前节点为孩子节点第一个节点 应为新生成的节点
  40. head = tNode;
  41. } else {
  42. // 否则应挂载上一个孩子节点的(head)的右树下
  43. head.right = tNode;
  44. }
  45. cur = head; // 缓存当前头
  46. cur.left = en(node.children); // 深度优先 建立根节点左树
  47. }
  48. return head;
  49. }
  50. // 将二叉树反序列化为 多叉树
  51. public Node decode(TreeNode root) {
  52. if (root == null) {
  53. return null;
  54. }
  55. return new Node(root.val, de(root.left));
  56. }
  57. private List<Node> de(TreeNode root) {
  58. ArrayList<Node> childern = new ArrayList<>();
  59. while (root != null) {
  60. Node cur = new Node(root.val, de(root.left)); //先深度优先 递归到左树叶子节点
  61. childern.add(cur); //将当前节点加入链表集合中
  62. root = root.right; //再while循环 将当前节点(即左树)的孩子节点 不断加入集合中
  63. }
  64. return childern;
  65. }
  66. }

8. 求二叉树最大宽度

使用宽度优先搜索

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int v) {
  6. value = v;
  7. }
  8. }
  9. public static int maxWidth(Node head) {
  10. if (head == null) {
  11. return 0;
  12. }
  13. Queue<Node> queue = new LinkedList<>();
  14. queue.add(head);
  15. int max = 0;
  16. int curWidth = 0; // 当前层宽度
  17. Node curEnd = head; // 当前层尾元素
  18. Node nextEnd = null; // 下一层尾元素
  19. while (!queue.isEmpty()) {
  20. Node cur = queue.poll();
  21. curWidth++; // 增加宽度
  22. if (cur.left != null) { // 是否有左右树
  23. queue.add(cur.left); // 入栈
  24. nextEnd = cur.left; // 更新下一层尾元素
  25. }
  26. if (cur.right != null) {
  27. queue.add(cur.right);
  28. nextEnd = cur.right;
  29. }
  30. if (cur == curEnd) {
  31. max = Math.max(max, curWidth);
  32. curWidth = 0; // 重置当前宽度
  33. curEnd = nextEnd; // 更新当前尾元素 为下一层尾元素
  34. }
  35. }
  36. return max;
  37. }

9. 求二叉树某个节点的后继节点

首先我们可以通过中序遍历的方式 知道某个节点的后续节点 根据中序遍历(左 头 右)的规则我们可以推断出

  1. 如果x节点有右树 后继节点为右孩子 最深的左节点
  2. 如果x节点为无右树 后继节点为离x节点最近的父辈节点并且该父辈节点以左节点形式存在

    1. 不断往上找父辈节点 并且此父辈节点是以左孩子形式存在 此时会命中 parent.rigth != nodewhile循环结束 返回此父辈节点为后继节点
    2. 不断往上找父辈节点 但父辈节点全为右孩子形式 此时会命中 parent == nullwhile循环结束 此时会返回父辈节点(此时父辈节点为null 当前节点没有后继节点)
  3. 如果x节点无右树并为左节点 后续节点为父节点

根据上述规则我们可以给每个节点添加一个parent指针(存储为父节点地址),通过parent指针可以找到此节点的父节点

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node parent;
  6. public Node(int data) {
  7. this.value = data;
  8. }
  9. }
  10. public static Node getSuccessorNode(Node node) {
  11. if (node == null) {
  12. return null;
  13. }
  14. // 如果查找的节点存在右孩子 查询右树的最深的左树
  15. if (node.right != null) {
  16. return getLeftMost(node.right);
  17. } else {
  18. // 如果查找的节点不存在右孩子
  19. Node parent = node.parent;//查询节点的父节点
  20. /**
  21. * 1.如果当前查找节点为父节点的左孩子 while循环不成立 直接返回父节点即可
  22. * 2.如果当前查找节点为父节点的右孩子 while循环成立 查找最近的父辈节点并且该父辈节点以左节点形式存在
  23. * 2.1 不断往上找父辈节点 并且此父辈节点是以左孩子形式存在 此时会命中 `parent.rigth != node`while循环结束 返回此父辈节点为后继节点
  24. * 2.2不断往上找父辈节点 但父辈节点全为右孩子形式 此时会命中 `parent == null`while循环结束 此时会返回父辈节点(此时父辈节点为null 当前节点没有后继节点)
  25. */
  26. while (parent != null && parent.right == node) {
  27. node = parent;
  28. parent = node.parent;
  29. }
  30. return parent;
  31. }
  32. }
  33. private static Node getLeftMost(Node node) {
  34. while (node.left != null) {
  35. node = node.left; // 最深的 左节点
  36. }
  37. return node;
  38. }

10. 折纸问题

请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开
此时折痕是凹下去的,即折痕突起的方向指向纸条的背面
如果从纸条的下边向上方连续对折2次,压出折痕后展开
此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次
请从上到下打印所有折痕的方向。
N=1时,打印: down
N=2时,打印: down down up

思路:

其实为一个比较特殊的二叉树的中序遍历,头节点为凹,左子树必定为凹,右子树必定为凸,满足以上条件中序遍历既可。

  1. public static void printAllFolds(int N) {
  2. process(1, N, true);
  3. System.out.println();
  4. }
  5. // 当前你来了一个节点,脑海中想象的!
  6. // 这个节点在第i层,一共有N层,N固定不变的
  7. // 这个节点如果是凹的话,down = T
  8. // 这个节点如果是凸的话,down = F
  9. // 函数的功能:中序打印以你想象的节点为头的整棵树!
  10. public static void process(int i, int N, boolean down) {
  11. if (i > N) {
  12. return;
  13. }
  14. process(i + 1, N, true);
  15. System.out.print(down ? "凹 " : "凸 ");
  16. process(i + 1, N, false);
  17. }
  18. public static void main(String[] args) {
  19. int N = 4;
  20. printAllFolds(N);
  21. }

11. 判断是否为完全二叉树

一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

特征:叶子结点只可能在最大的两层出,即叶子节点从左到右中间不可能出现空缺,要么出现在最后第二层和第一层。如叶子节点往下遇到null后往后的叶子节点左右节点必须为null,满足此规则即可。

如下图所示

11.1. 11. 二叉树 - 图3

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int data) {
  6. this.value = data;
  7. }
  8. }
  9. public static boolean isCBT1(Node head) {
  10. if (head == null) {
  11. return true;//空树 也为完全二叉树
  12. }
  13. LinkedList<Node> queue = new LinkedList<>();
  14. // 是否遇到过左右两个孩子不双全的节点 标记变量
  15. boolean leaf = false;
  16. Node l = null;
  17. Node r = null;
  18. queue.add(head); //入队列
  19. while (!queue.isEmpty()) {
  20. head = queue.poll();
  21. l = head.left;
  22. r = head.right;
  23. if (
  24. // 如果遇到了不双全的节点之后,又发现当前节点不是叶节点
  25. (leaf && (l != null || r != null)) //leaf变为true 说明之前遇到了null节点
  26. ||
  27. (l == null && r != null) //左树为null 右树不为null 根节点出现空缺返回false
  28. ) {
  29. return false;
  30. }
  31. //有左孩子 入队列
  32. if (l != null) {
  33. queue.add(l);
  34. }
  35. //有右孩子 入队列
  36. if (r != null) {
  37. queue.add(r);
  38. }
  39. //因为是按层遍历 到根节点了 标记变量改为true
  40. if (l == null || r == null) {
  41. leaf = true;
  42. }
  43. }
  44. return true;
  45. }

12. 二叉树的递归套路

  1. 假设以X节点为头,假设可以向X左树和X右树要任何信息
  2. 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
  3. 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
  4. 把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
  5. 递归函数都返回S,每一棵子树都这么要求
  6. 写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息

13. 求二叉树最大距离

给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离

最大距离必须为最优路径

  1. 最大距离存在左树中 不经过根节点
  2. 最大距离存在右树中 不经过根节点
  3. 最大距离不存在左右树中 必经过根节点 左树高度 + 右树高度 + 1
  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int data) {
  6. this.value = data;
  7. }
  8. }
  9. public static class info{
  10. public int maxDistance; //最大距离
  11. public int height; //高度
  12. public info(int maxDistance, int height) {
  13. this.maxDistance = maxDistance;
  14. this.height = height;
  15. }
  16. }
  17. public static int maxDistance2(Node head) {
  18. return process(head).maxDistance;
  19. }
  20. //深度优先
  21. private static info process(Node head) {
  22. if(head == null) {
  23. return new info(0, 0); //为空null 高度为0 最大距离为0
  24. }
  25. info leftInfo = process(head.left);
  26. info rightInfo = process(head.right);
  27. int height = Math.max(leftInfo.height, rightInfo.height)+1; //高度更新
  28. int p1 = leftInfo.maxDistance;
  29. int p2 = rightInfo.maxDistance;
  30. // 如为叶子节点 则会命中此语句 携带此数 递归下去 找到最大距离
  31. int p3 = leftInfo.height + rightInfo.height + 1;
  32. int maxDistance = Math.max(Math.max(p1, p2), p3); //最大距离
  33. return new info(maxDistance,height); //返回 携带信息
  34. }

14. 判断是否为满二叉树

判断方法一:收集整颗树的高度和节点数,只有满二叉树满足 2^h-1 = n

判断方法二:左树 和 右树为满,并且左右树高度一致,则为满二叉树

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int data) {
  6. this.value = data;
  7. }
  8. }
  9. public static class info1 {
  10. int height;
  11. int nodes;
  12. public info1(int height, int nodes) {
  13. this.height = height;
  14. this.nodes = nodes;
  15. }
  16. }
  17. // 只有满二叉树满足 : 2 ^ h - 1 == n
  18. public static boolean isFull1(Node head) {
  19. if (head == null) {
  20. return true;
  21. }
  22. info1 allInfo = process1(head);
  23. return (1 << allInfo.height) - 1 == allInfo.nodes;
  24. }
  25. private static info1 process1(Node head) {
  26. if (head == null) {
  27. return new info1(0, 0); // 节点空返回高度0节点数0
  28. }
  29. info1 leftInfo = process1(head.left); // 获取左右树信息
  30. info1 rightInfo = process1(head.right);
  31. int height = Math.max(leftInfo.height, rightInfo.height) + 1; // 获取最大高度 +自身
  32. int nodes = leftInfo.nodes + rightInfo.nodes + 1; // 左右节点数 +自身
  33. return new info1(height, nodes); // 返回给上层
  34. }
  35. // 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的
  36. public static boolean isFull2(Node head) {
  37. if (head == null) {
  38. return true;
  39. }
  40. return process2(head).isFull;
  41. }
  42. public static class info2 {
  43. public boolean isFull;
  44. public int height;
  45. public info2(boolean isFull, int height) {
  46. this.isFull = isFull;
  47. this.height = height;
  48. }
  49. }
  50. private static info2 process2(Node head) {
  51. if (head == null) {
  52. return new info2(true, 0);
  53. }
  54. info2 leftInfo = process2(head.left);
  55. info2 rightInfo = process2(head.right);
  56. boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
  57. int height = Math.max(leftInfo.height, rightInfo.height) + 1;
  58. return new info2(isFull, height);
  59. }

15. 求二叉树中最大的二叉搜索子树的大小

给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的大小

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int data) {
  6. this.value = data;
  7. }
  8. }
  9. public static class info {
  10. public int maxBSTSubtreeSize;// 最大搜索子树大小
  11. public int allSize;// 所有子节点数量
  12. public int max;
  13. public int min;
  14. public info(int maxBSTSubtreeSize, int allSize, int max, int min) {
  15. this.maxBSTSubtreeSize = maxBSTSubtreeSize;
  16. this.allSize = allSize;
  17. this.max = max;
  18. this.min = min;
  19. }
  20. }
  21. public static info process(Node head) {
  22. if (head == null) {
  23. return null;
  24. }
  25. info leftInfo = process(head.left);
  26. info rightInfo = process(head.right);
  27. int max = head.value; // 假设为自身
  28. int min = head.value;
  29. int allSize = 1; // 所有字节节数 算上自身
  30. if (leftInfo != null) {
  31. max = Math.max(max, leftInfo.max);
  32. min = Math.min(min, leftInfo.min);
  33. allSize += leftInfo.allSize; // 加上左树的子节点数
  34. }
  35. if (rightInfo != null) {
  36. max = Math.max(max, rightInfo.max);
  37. min = Math.min(min, rightInfo.min);
  38. allSize += rightInfo.allSize; //加上右树的字节点数
  39. }
  40. int p1 = -1;
  41. if (leftInfo != null) {
  42. p1 = leftInfo.maxBSTSubtreeSize;
  43. }
  44. int p2 = -1;
  45. if (rightInfo != null) {
  46. p2 = rightInfo.maxBSTSubtreeSize;
  47. }
  48. int p3 = -1;
  49. //判断最大子树大小 与 所有节点数 是否相等(即没有断层)
  50. boolean leftBst = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize);
  51. boolean rightBst = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize);
  52. if (leftBst && rightBst) {
  53. // 验证当前节点是否满足二叉树规则 左孩子比自身小 右孩子比自身大
  54. boolean leftMaxLeesX = leftInfo == null ? true : (leftInfo.max < head.value);
  55. boolean rightMinMoreX = rightInfo == null ? true : (head.value < rightInfo.min);
  56. if (leftMaxLeesX && rightMinMoreX) {
  57. //如果最大搜索子树大小没断层 并且满足搜索二叉树规则 进行递加
  58. int leftSize = leftInfo == null ? 0 : leftInfo.allSize;
  59. int rightSize = rightInfo == null ? 0 : rightInfo.allSize;
  60. p3 = leftSize + rightSize + 1;
  61. }
  62. }
  63. return new info(Math.max(p1, Math.max(p2, p3)), allSize, max, min);
  64. }
  65. public static int maxSubBSTSize2(Node head) {
  66. if(head == null) {
  67. return 0;
  68. }
  69. return process(head).maxBSTSubtreeSize;
  70. }

16. 递归套路版判断是否为完全二叉树

上面我们使用队列按层遍历(bfs)来判断是否为完全二叉树,我们可以改为递归版本使用递归套路

17. 求二叉树最大的二叉搜索树的头节点

给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的头节点

18. 求二叉树上a和b两节点的最低公共祖先

给定一棵二叉树的头节点head,和另外两个节点a和b,返回a和b的最低公共祖先

  1. public static class Node {
  2. public int value;
  3. public Node left;
  4. public Node right;
  5. public Node(int data) {
  6. this.value = data;
  7. }
  8. }
  9. public static class info {
  10. boolean findA;
  11. boolean findB;
  12. Node ans;
  13. public info(boolean findA, boolean findB, Node ans) {
  14. this.findA = findA;
  15. this.findB = findB;
  16. this.ans = ans;
  17. }
  18. }
  19. public static Node lowestAncestor2(Node head, Node a, Node b) {
  20. if (head == null) {
  21. return null;
  22. }
  23. return process(head, a, b).ans;
  24. }
  25. private static info process(Node head, Node a, Node b) {
  26. if (head == null) {
  27. return new info(false, false, null); // 空节点 返回info信息
  28. }
  29. info leftInfo = process(head.left, a, b); // 递归下去
  30. info rightInfo = process(head.right, a, b);
  31. boolean findA = (head == a) || leftInfo.findA || rightInfo.findA; // 如果a/b为自身 或者 左右info之前发现过a/b为true
  32. boolean findB = (head == b) || leftInfo.findB || rightInfo.findB;
  33. Node ans = null; // 先初始化
  34. if (findA && findB) { // a和b都标记找到
  35. if (leftInfo.ans != null) { // 之前leftInfo有标记过祖先,继承之前的祖先,因为题目要求是最低祖先(最先找到的祖先)
  36. ans = leftInfo.ans;
  37. } else if (rightInfo.ans != null) { // right同理
  38. ans = rightInfo.ans;
  39. } else {
  40. ans = head; // 之前都没有标记过祖先 即当前节点为最先发现的祖先
  41. }
  42. }
  43. return new info(findA, findB, ans); // 返回info信息
  44. }

19. 派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树
树的头节点是公司唯一的老板,除老板之外的每个员工都有唯一的直接上级
叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外每个员工都有一个或多个直接下级
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:

  1. 如果某个员工来了,那么这个员工的所有直接下级都不能来

  2. 派对的整体快乐值是所有到场员工快乐值的累加

  3. 你的目标是让派对的整体快乐值尽量大

给定一棵多叉树的头节点boss,请返回派对的最大快乐值。

  1. public static class Employee {
  2. public int happy;
  3. public List<Employee> nexts;
  4. public Employee(int h) {
  5. happy = h;
  6. nexts = new ArrayList<>();
  7. }
  8. }
  9. public static class info {
  10. int no; // 当前节点不来的情况
  11. int yes; // 当前节点来的情况
  12. public info(int no, int yes) {
  13. super();
  14. this.no = no;
  15. this.yes = yes;
  16. }
  17. }
  18. public static int maxHappy2(Employee head) {
  19. info allInfo = process(head);
  20. return Math.max(allInfo.no, allInfo.yes);
  21. }
  22. private static info process(Employee head) {
  23. if (head == null) {
  24. return new info(0, 0);
  25. }
  26. int no = 0;// 不来的情况
  27. int yes = head.happy; // 来的情况
  28. for (Employee employee : head.nexts) {
  29. info nextInfo = process(employee);
  30. no += Math.max(nextInfo.no, nextInfo.yes);// 子节点可来可不来
  31. yes += nextInfo.no; // 当前节点来了 他的下属子节点必须得不来
  32. }
  33. return new info(no, yes);
  34. }

20. 100. 相同的树

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public boolean isSameTree(TreeNode p, TreeNode q) {
  18. if (p == null ^ q == null) { //有一个为空 另外一个不为空 返回
  19. return false;
  20. }
  21. if (p == null && q == null) { //两者都为空
  22. return true;
  23. }
  24. //两者值相等 && 左树相等 && 右树相等
  25. return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
  26. }
  27. }

21. 101. 对称二叉树

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {}
  6. TreeNode(int val) { this.val = val; }
  7. TreeNode(int val, TreeNode left, TreeNode right) {
  8. this.val = val;
  9. this.left = left;
  10. this.right = right;
  11. }
  12. }
  13. class Solution {
  14. public boolean isSymmetric(TreeNode root) {
  15. return isSameTree(root,root);//自己与自己比较
  16. }
  17. public boolean isSameTree(TreeNode p, TreeNode q) {
  18. if (p == null ^ q == null) { //有一个为空 另外一个不为空 返回
  19. return false;
  20. }
  21. if (p == null && q == null) { //两者都为空
  22. return true;
  23. }
  24. //两者值相等 && 左树与`右树相等 && 右树与`左树相等
  25. return p.val == q.val && isSameTree(p.left, q.right) && isSameTree(p.right, q.left);
  26. }
  27. }

22. 104. 二叉树的最大深度

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public int maxDepth(TreeNode root) {
  18. if (root == null) {
  19. return 0;
  20. }
  21. return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
  22. }
  23. }

23. 105. 从前序与中序遍历序列构造二叉树

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public TreeNode buildTree(int[] preorder, int[] inorder) {
  18. if (preorder == null || inorder == null || preorder.length != inorder.length) {
  19. return null;
  20. }
  21. Map<Integer, Integer> IndexMap = new HashMap<>();
  22. for (int i = 0; i < inorder.length; i++) {
  23. IndexMap.put(inorder[i], i);
  24. }
  25. return f(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1,IndexMap);
  26. }
  27. private TreeNode f(int[] preorder, int l1, int r1, int[] inorder, int l2, int r2,Map<Integer, Integer> IndexMap) {
  28. if (l1 > r1) { //
  29. return null;
  30. }
  31. TreeNode head = new TreeNode(preorder[l1]); // 头节点
  32. if (l1 == r1) { // 只有一个元素时
  33. return head;
  34. }
  35. int index = IndexMap.get(preorder[l1]); // 查找当前头节点在中序遍历数组的位置
  36. // 左树构建 l1+1到 l1 + index - l2 为左树的先序遍历范围 l2到index-1为中序遍历范围
  37. head.left = f(preorder, l1 + 1, l1 + index - l2, inorder, l2, index - 1,IndexMap); // 注意左树和右树构建采用的遍历范围均相同长度
  38. // 右树构建 l1 + index - l2(即左树的结束范围) +1 到 r1为右树先序遍历范围 index+1到r2为右树中序遍历的范围
  39. head.right = f(preorder, l1 + index - l2 + 1, r1, inorder, index + 1, r2,IndexMap);
  40. return head;
  41. }
  42. }

24. 107. 二叉树的层序遍历 II

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public List<List<Integer>> levelOrderBottom(TreeNode root) {
  18. List<List<Integer>> ans = new ArrayList<>();
  19. if (root == null) { // 根节点为空直接返回空列表
  20. return ans;
  21. }
  22. LinkedList<TreeNode> queue = new LinkedList<>();
  23. queue.add(root); // 将头节点加入队列中
  24. while (!queue.isEmpty()) {
  25. int size = queue.size(); // 获取队列当前长度
  26. List<Integer> tans = new LinkedList<>();
  27. for (int i = 0; i < size; i++) {
  28. TreeNode temp = queue.poll();
  29. tans.add(temp.val); // 获取头节点的值 放入局部队列中
  30. if (temp.left != null) {
  31. queue.add(temp.left); // 左节点非空 加入到队列中
  32. }
  33. if (temp.right != null) {
  34. queue.add(temp.right); // 右节点非空 加入队列中
  35. }
  36. }
  37. ans.add(0, tans); // 将上次队列缓存头节点清空后的节点值加入到结果集合中
  38. }
  39. return ans;
  40. }
  41. }

25. 110. 平衡二叉树

平衡二叉树:一个二叉树每个节点左右两个子树的高度差的绝对值不超过 1

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public boolean isBalanced(TreeNode root) {
  18. return process(root).isBalanced; //直接返回root节点的信息
  19. }
  20. public class info {
  21. public boolean isBalanced; // 是否为平衡二叉树
  22. public int height; // 高度
  23. public info(boolean isBalanced, int height) {
  24. this.isBalanced = isBalanced;
  25. this.height = height;
  26. }
  27. }
  28. public info process(TreeNode root) {
  29. if (root == null) { // 空节点
  30. return new info(true, 0);
  31. }
  32. info leftInfo = process(root.left); // 左树信息
  33. info rightInfo = process(root.right); // 右树信息
  34. int height = Math.max(leftInfo.height, rightInfo.height) + 1; // 当前节点高度为 左和右树最大值 +1
  35. boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced
  36. && Math.abs(leftInfo.height - rightInfo.height) <= 1; //左树和右树为平衡二叉树 并且 左右树高度差不大于1
  37. return new info(isBalanced, height);
  38. }
  39. }

26. 98. 验证二叉搜索树

搜索二叉树:左树的值比根节点的值小,右树的值比根节点的值大

一颗搜索二叉树中序遍历是递增顺序

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public boolean isValidBST(TreeNode root) {
  18. return isBST(root).isBst;
  19. }
  20. public class info{
  21. boolean isBst;
  22. int max;
  23. int min;
  24. public info(boolean isBst, int max, int min) {
  25. this.isBst = isBst;
  26. this.max = max;
  27. this.min = min;
  28. }
  29. }
  30. public info isBST(TreeNode root) {
  31. if(root == null) { //头节点为空则返回空
  32. return null;
  33. }
  34. info leftinfo = isBST(root.left); //递归进入左树
  35. info rightinfo = isBST(root.right); //递归进入右树
  36. int max = root.val; //假设最大值为自身
  37. int min = root.val;//假设最小值为自身
  38. if(leftinfo != null) { //左树非空
  39. max = Math.max(max, leftinfo.max); //提取左树信息
  40. min = Math.min(min, leftinfo.min);
  41. }
  42. if(rightinfo != null) {
  43. max = Math.max(max, rightinfo.max); //提取右树信息
  44. min = Math.min(min, rightinfo.min);
  45. }
  46. boolean bst = true; //先假设为搜索二叉树
  47. if(leftinfo !=null && !leftinfo.isBst) { //如果左树不满足则 头节点标记为不是二叉树
  48. bst =false;
  49. }
  50. if(rightinfo !=null && !rightinfo.isBst) { //如果右树不满足则 头节点标记为不是二叉树
  51. bst =false;
  52. }
  53. boolean lefiBst = leftinfo == null ? true : (leftinfo.max < root.val); //如果左树信息为空返回ture 否则左树最大值必须小于当前节点值
  54. boolean rightBst = rightinfo == null ? true : (rightinfo.min > root.val);//如果右树信息为空返回ture 否则右树最小值必须大于当前节点值
  55. if(!lefiBst || !rightBst) { //不满足搜索二叉树规则
  56. bst = false;
  57. }
  58. return new info(bst, max, min);
  59. }
  60. }

27. 112. 路径总和

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public boolean isSum = false;
  18. public boolean hasPathSum(TreeNode root, int targetSum) {
  19. if(root ==null) { //根节点为空 直接返回false
  20. return false;
  21. }
  22. tree(root, 0, targetSum);
  23. return isSum;
  24. }
  25. public void tree(TreeNode root, int preSum, int sum) {
  26. if (root.left == null && root.right == null) { //当前头节点左右子节点都为空
  27. if (preSum + root.val == sum) { //当前头节点值与上次sum 为target则找到
  28. isSum = true;
  29. }
  30. return;
  31. }
  32. preSum += root.val; //累加当前sum
  33. if (root.left != null) { //左节点有叶节点 递归进去
  34. tree(root.left, preSum, sum);
  35. }
  36. if (root.right != null) { //右节点有叶节点 递归进去
  37. tree(root.right, preSum, sum);
  38. }
  39. }
  40. }

28. 113. 路径总和 II

  1. public class TreeNode {
  2. int val;
  3. TreeNode left;
  4. TreeNode right;
  5. TreeNode() {
  6. }
  7. TreeNode(int val) {
  8. this.val = val;
  9. }
  10. TreeNode(int val, TreeNode left, TreeNode right) {
  11. this.val = val;
  12. this.left = left;
  13. this.right = right;
  14. }
  15. }
  16. class Solution {
  17. public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
  18. List<List<Integer>> ans = new ArrayList<>();
  19. if (root == null) {
  20. return ans;
  21. }
  22. ArrayList<Integer> path = new ArrayList<>();
  23. process(root, path, 0, targetSum, ans);
  24. return ans;
  25. }
  26. public void process(TreeNode root, ArrayList<Integer> path, int preSum, int sum, List<List<Integer>> ans) {
  27. if (root.left == null & root.right == null) {
  28. if (root.val + preSum == sum) {
  29. path.add(root.val); // 添加当前节点路径
  30. // List<Integer> newPath = copy(path); // 拷贝
  31. List<Integer> newPath = (List<Integer>) path.clone(); // 本质上是浅拷贝 但里面存储的是值 所以复制了里面值 如里面存储的是引用不应用深拷贝
  32. ans.add(newPath); // 添加到结果集合中
  33. path.remove(path.size() - 1); // 删除当前路径
  34. }
  35. }
  36. preSum += root.val; // 累加 无需回溯 因为是值传递 不是引用传递
  37. path.add(root.val); // 记录节点
  38. if (root.left != null) { // 递归
  39. process(root.left, path, preSum, sum, ans);
  40. }
  41. if (root.right != null) { // 递归
  42. process(root.right, path, preSum, sum, ans);
  43. }
  44. path.remove(path.size() - 1); // 恢复现场
  45. }
  46. }