- 1. 二叉树
- 2. 递归序
- 3. 非递归版遍历二叉树顺序
- 4. 前缀树
- 5. 二叉树按层变遍历
- 6. 二叉树序列化和反序列化
- 431.Encode N-ary Tree to Binary Tree">7. 431.Encode N-ary Tree to Binary Tree
- 8. 求二叉树最大宽度
- 9. 求二叉树某个节点的后继节点
- 10. 折纸问题
- 11. 判断是否为完全二叉树
- 12. 二叉树的递归套路
- 13. 求二叉树最大距离
- 14. 判断是否为满二叉树
- 15. 求二叉树中最大的二叉搜索子树的大小
- 16. 递归套路版判断是否为完全二叉树
- 17. 求二叉树最大的二叉搜索树的头节点
- 18. 求二叉树上a和b两节点的最低公共祖先
- 19. 派对的最大快乐值
- 100. 相同的树">20. 100. 相同的树
- 101. 对称二叉树">21. 101. 对称二叉树
- 104. 二叉树的最大深度">22. 104. 二叉树的最大深度
- 105. 从前序与中序遍历序列构造二叉树">23. 105. 从前序与中序遍历序列构造二叉树
- 107. 二叉树的层序遍历 II">24. 107. 二叉树的层序遍历 II
- 110. 平衡二叉树">25. 110. 平衡二叉树
- 98. 验证二叉搜索树">26. 98. 验证二叉搜索树
- 112. 路径总和">27. 112. 路径总和
- 113. 路径总和 II">28. 113. 路径总和 II
1. 二叉树
2. 递归序
先序遍历 : 头节点 左节点 右节点
中序遍历 : 左节点 头节点 右节点
后序遍历 : 左节点 右节点 头节点
public static class Node {public int val;public Node left;public Node right;public Node(int val) {this.val = val;}}public static void f(Node head) {if(head == null) {return;}System.out.println("先序遍历"+head.val); //头节点 左节点 右节点f(head.left);// System.out.println("中序遍历"+head.val); //左节点 头节点 右节点f(head.right);// System.out.println("后序遍历"+head.val); //左节点 右节点 头节点}public static void main(String[] args) {Node head = new Node(1);head.left = new Node(2);head.right = new Node(3);head.left.left = new Node(4);head.left.right = new Node(5);head.right.left = new Node(6);head.right.right = new Node(8);f(head);}
3. 非递归版遍历二叉树顺序
使用Stack类 手动压栈出栈
3.1. 先序
- push头节点到栈顶 再pop弹出并输出
- 先push右节点 到栈中 压为栈底
- 再push左节点 到栈中 压为栈顶 重复上述步骤
//先序遍历 头 左 右public static void pre(Node head) {System.out.println("先序遍历");if(head != null) {Stack<Node> stack = new Stack<>();stack.add(head);while(!stack.isEmpty()) {head = stack.pop(); //删除栈顶System.out.println(head.value);//先push右树 压入栈底if(head.right != null) {stack.push(head.right);}//再push左树 压入栈顶if(head.left != null) {stack.push(head.left);}}}}
3.2. 2个栈实现后序
上面我们使用1个栈实现了,先序遍历头左右的顺序,我们知道后序遍历为左右头,我们微调一下压入左右树的顺序,实现了头右左的顺序,准备第二个栈按顺序压入,再进行弹出成了左右头的顺序。
//两个栈 实现后序遍历public static void pos1(Node head) {System.out.println("后序遍历");if(head !=null) {Stack<Node> s1= new Stack<>();Stack<Node> s2= new Stack<>();s1.add(head);while(!s1.isEmpty()) {head = s1.pop();s2.push(head);//头节点压入栈 s2栈底if(head.left != null) {s1.push(head.left); //左节点 入s1栈底}if(head.right != null) { //右节点 入s1栈 此时为栈顶s1.push(head);}//此时s1栈存储为 [右,左]//s2栈为 [头] 两栈结合为 [左,右,头]}// 左 右 头while(!s2.isEmpty()) {System.out.println(s2.pop().value);}}}
3.3. 1个栈实现后序
// 一个栈 实现后序遍历public static void pos2(Node head) {System.out.println("后序遍历2");if (head != null) {Stack<Node> stack = new Stack<>();stack.push(head);Node cur = null;Node pre = null;while (!stack.isEmpty()) {cur = stack.peek(); // 只查看栈顶不删除// 不断将左树入栈 栈顶为左树的叶子节点 pre为了防止之前入过栈的左右节点 重新入栈if (cur.left != null && pre != cur.left && pre != cur.right) {stack.push(cur.left);// 左树无节点/已经入过栈 将右树入栈} else if (cur.right != null && pre != cur.right) {stack.push(cur.right);} else {// 左右都无节点 直接删除当前栈顶 为头节点System.out.println(stack.pop().value);pre = cur;}}}}
3.4. 中序
// 中序遍历 左 头 右public static void in(Node cur) {System.out.println("中序遍历");if (cur != null) {Stack<Node> statck = new Stack<>();while (!statck.isEmpty() || cur != null) {if (cur != null) {statck.push(cur);cur = cur.left; // 遍历根节点 左树叶子节点 入栈} else {cur = statck.pop(); // 弹出栈System.out.println(cur.value);cur = cur.right;}}}}
4. 前缀树
5. 二叉树按层变遍历
即宽度优先搜索(BFS) 使用队列实现
//按层遍历二叉树 bfspublic static void levle(Node head) {if(head ==null) {return;}LinkedList<Node> queue = new LinkedList<>();queue.add(head);while(!queue.isEmpty()) {Node cur = queue.poll();System.out.println(cur.value);if(cur.left != null) {queue.add(head.left);}if(cur.right != null) {queue.add(head.right);}}}
6. 二叉树序列化和反序列化
6.1. 先序遍历序列化
只有先序和后序遍历可以序列化,而中序遍历序列化时会有歧义(即两个节点位置不确定)
// 先序 序列化二叉树public static Queue<String> preSerial(Node head) {if (head == null) {return null;}Queue<String> queue = new LinkedList<>();pres(head, queue);return queue;}private static void pres(Node head, Queue<String> queue) {if (head == null) {queue.add(null); // null节点 入栈} else {queue.add(String.valueOf(head.value)); // 头节点压栈pres(head.left, queue);pres(head.right, queue);}}
6.2. 按层遍历序列化
// 先序 反序列化二叉树public static Node buildByPreQueue(Queue<String> queue) {if (queue == null || queue.size() == 0) {return null;}return preb(queue);}private static Node preb(Queue<String> queue) {String value = queue.poll();if (value == null) {return null;}Node head = new Node(Integer.valueOf(value));head.left = preb(queue);head.right = preb(queue);return head;}
7. 431.Encode N-ary Tree to Binary Tree
设计一种算法,将 N 叉树编码为二叉树,并对二叉树进行解码,得到原始 N 叉树。N-ary 树是一个有根树,其中每个节点不超过 N 个子节点。类似地,二叉树是一个有根树,其中每个节点的子节点不超过 2 个。您的编码/解码算法的工作方式没有限制。您只需要确保可以将 N 叉树编码为二叉树,并且可以将二叉树解码为原始的 N 叉树结构。
例如,您可以通过3-ary这种方式将以下树编码为二叉树:

请注意,以上只是一个可能会或可能不会起作用的示例。您不一定需要遵循这种格式,因此请发挥创造力并自己提出不同的方法。
思路:
将多叉树的孩子节点 全部挂载左树的右边界
public static class Node {public int val;public List<Node> children;public Node() {}public Node(int _val) {val = _val;}public Node(int _val, List<Node> _children) {val = _val;children = _children; // 节点下所有孩子(即有多少个这个节点就有多少叉)}};public static class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}}class Codec {// 将多叉树 序列化为二叉树public TreeNode encode(Node root) {if (root == null) {return null;}TreeNode head = new TreeNode(root.val);// 先建立 根节点head.left = en(root.children); // 将孩子节点(多叉树)收为二叉树return head;}// 孩子节点第一个全部挂载左树 剩下的孩子节点挂载在新建左树节点的右树private TreeNode en(List<Node> children) {TreeNode head = null;TreeNode cur = null;for (Node node : children) {TreeNode tNode = new TreeNode(node.val); // 当前孩子 新生成节点if (head == null) {// 当前节点为孩子节点第一个节点 应为新生成的节点head = tNode;} else {// 否则应挂载上一个孩子节点的(head)的右树下head.right = tNode;}cur = head; // 缓存当前头cur.left = en(node.children); // 深度优先 建立根节点左树}return head;}// 将二叉树反序列化为 多叉树public Node decode(TreeNode root) {if (root == null) {return null;}return new Node(root.val, de(root.left));}private List<Node> de(TreeNode root) {ArrayList<Node> childern = new ArrayList<>();while (root != null) {Node cur = new Node(root.val, de(root.left)); //先深度优先 递归到左树叶子节点childern.add(cur); //将当前节点加入链表集合中root = root.right; //再while循环 将当前节点(即左树)的孩子节点 不断加入集合中}return childern;}}
8. 求二叉树最大宽度
使用宽度优先搜索
public static class Node {public int value;public Node left;public Node right;public Node(int v) {value = v;}}public static int maxWidth(Node head) {if (head == null) {return 0;}Queue<Node> queue = new LinkedList<>();queue.add(head);int max = 0;int curWidth = 0; // 当前层宽度Node curEnd = head; // 当前层尾元素Node nextEnd = null; // 下一层尾元素while (!queue.isEmpty()) {Node cur = queue.poll();curWidth++; // 增加宽度if (cur.left != null) { // 是否有左右树queue.add(cur.left); // 入栈nextEnd = cur.left; // 更新下一层尾元素}if (cur.right != null) {queue.add(cur.right);nextEnd = cur.right;}if (cur == curEnd) {max = Math.max(max, curWidth);curWidth = 0; // 重置当前宽度curEnd = nextEnd; // 更新当前尾元素 为下一层尾元素}}return max;}
9. 求二叉树某个节点的后继节点
首先我们可以通过中序遍历的方式 知道某个节点的后续节点 根据中序遍历(左 头 右)的规则我们可以推断出
- 如果x节点有右树 后继节点为右孩子 最深的左节点
如果x节点为无右树 后继节点为离x节点最近的父辈节点并且该父辈节点以左节点形式存在
- 不断往上找父辈节点 并且此父辈节点是以左孩子形式存在 此时会命中
parent.rigth != nodewhile循环结束 返回此父辈节点为后继节点 - 不断往上找父辈节点 但父辈节点全为右孩子形式 此时会命中
parent == nullwhile循环结束 此时会返回父辈节点(此时父辈节点为null 当前节点没有后继节点)
- 不断往上找父辈节点 并且此父辈节点是以左孩子形式存在 此时会命中
- 如果x节点无右树并为左节点 后续节点为父节点
根据上述规则我们可以给每个节点添加一个parent指针(存储为父节点地址),通过parent指针可以找到此节点的父节点
public static class Node {public int value;public Node left;public Node right;public Node parent;public Node(int data) {this.value = data;}}public static Node getSuccessorNode(Node node) {if (node == null) {return null;}// 如果查找的节点存在右孩子 查询右树的最深的左树if (node.right != null) {return getLeftMost(node.right);} else {// 如果查找的节点不存在右孩子Node parent = node.parent;//查询节点的父节点/*** 1.如果当前查找节点为父节点的左孩子 while循环不成立 直接返回父节点即可* 2.如果当前查找节点为父节点的右孩子 while循环成立 查找最近的父辈节点并且该父辈节点以左节点形式存在* 2.1 不断往上找父辈节点 并且此父辈节点是以左孩子形式存在 此时会命中 `parent.rigth != node`while循环结束 返回此父辈节点为后继节点* 2.2不断往上找父辈节点 但父辈节点全为右孩子形式 此时会命中 `parent == null`while循环结束 此时会返回父辈节点(此时父辈节点为null 当前节点没有后继节点)*/while (parent != null && parent.right == node) {node = parent;parent = node.parent;}return parent;}}private static Node getLeftMost(Node node) {while (node.left != null) {node = node.left; // 最深的 左节点}return node;}
10. 折纸问题
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开
此时折痕是凹下去的,即折痕突起的方向指向纸条的背面
如果从纸条的下边向上方连续对折2次,压出折痕后展开
此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。
给定一个输入参数N,代表纸条都从下边向上方连续对折N次
请从上到下打印所有折痕的方向。
N=1时,打印: down
N=2时,打印: down down up
思路:
其实为一个比较特殊的二叉树的中序遍历,头节点为凹,左子树必定为凹,右子树必定为凸,满足以上条件中序遍历既可。
public static void printAllFolds(int N) {process(1, N, true);System.out.println();}// 当前你来了一个节点,脑海中想象的!// 这个节点在第i层,一共有N层,N固定不变的// 这个节点如果是凹的话,down = T// 这个节点如果是凸的话,down = F// 函数的功能:中序打印以你想象的节点为头的整棵树!public static void process(int i, int N, boolean down) {if (i > N) {return;}process(i + 1, N, true);System.out.print(down ? "凹 " : "凸 ");process(i + 1, N, false);}public static void main(String[] args) {int N = 4;printAllFolds(N);}
11. 判断是否为完全二叉树
一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
特征:叶子结点只可能在最大的两层出,即叶子节点从左到右中间不可能出现空缺,要么出现在最后第二层和第一层。如叶子节点往下遇到null后往后的叶子节点左右节点必须为null,满足此规则即可。
如下图所示
11.1. 
public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static boolean isCBT1(Node head) {if (head == null) {return true;//空树 也为完全二叉树}LinkedList<Node> queue = new LinkedList<>();// 是否遇到过左右两个孩子不双全的节点 标记变量boolean leaf = false;Node l = null;Node r = null;queue.add(head); //入队列while (!queue.isEmpty()) {head = queue.poll();l = head.left;r = head.right;if (// 如果遇到了不双全的节点之后,又发现当前节点不是叶节点(leaf && (l != null || r != null)) //leaf变为true 说明之前遇到了null节点||(l == null && r != null) //左树为null 右树不为null 根节点出现空缺返回false) {return false;}//有左孩子 入队列if (l != null) {queue.add(l);}//有右孩子 入队列if (r != null) {queue.add(r);}//因为是按层遍历 到根节点了 标记变量改为trueif (l == null || r == null) {leaf = true;}}return true;}
12. 二叉树的递归套路
- 假设以X节点为头,假设可以向X左树和X右树要任何信息
- 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
- 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
- 把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
- 递归函数都返回S,每一棵子树都这么要求
- 写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息
13. 求二叉树最大距离
给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离
最大距离必须为最优路径
- 最大距离存在左树中 不经过根节点
- 最大距离存在右树中 不经过根节点
- 最大距离不存在左右树中 必经过根节点 左树高度 + 右树高度 + 1
public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static class info{public int maxDistance; //最大距离public int height; //高度public info(int maxDistance, int height) {this.maxDistance = maxDistance;this.height = height;}}public static int maxDistance2(Node head) {return process(head).maxDistance;}//深度优先private static info process(Node head) {if(head == null) {return new info(0, 0); //为空null 高度为0 最大距离为0}info leftInfo = process(head.left);info rightInfo = process(head.right);int height = Math.max(leftInfo.height, rightInfo.height)+1; //高度更新int p1 = leftInfo.maxDistance;int p2 = rightInfo.maxDistance;// 如为叶子节点 则会命中此语句 携带此数 递归下去 找到最大距离int p3 = leftInfo.height + rightInfo.height + 1;int maxDistance = Math.max(Math.max(p1, p2), p3); //最大距离return new info(maxDistance,height); //返回 携带信息}
14. 判断是否为满二叉树
判断方法一:收集整颗树的高度和节点数,只有满二叉树满足 2^h-1 = n
判断方法二:左树 和 右树为满,并且左右树高度一致,则为满二叉树
public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static class info1 {int height;int nodes;public info1(int height, int nodes) {this.height = height;this.nodes = nodes;}}// 只有满二叉树满足 : 2 ^ h - 1 == npublic static boolean isFull1(Node head) {if (head == null) {return true;}info1 allInfo = process1(head);return (1 << allInfo.height) - 1 == allInfo.nodes;}private static info1 process1(Node head) {if (head == null) {return new info1(0, 0); // 节点空返回高度0节点数0}info1 leftInfo = process1(head.left); // 获取左右树信息info1 rightInfo = process1(head.right);int height = Math.max(leftInfo.height, rightInfo.height) + 1; // 获取最大高度 +自身int nodes = leftInfo.nodes + rightInfo.nodes + 1; // 左右节点数 +自身return new info1(height, nodes); // 返回给上层}// 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的public static boolean isFull2(Node head) {if (head == null) {return true;}return process2(head).isFull;}public static class info2 {public boolean isFull;public int height;public info2(boolean isFull, int height) {this.isFull = isFull;this.height = height;}}private static info2 process2(Node head) {if (head == null) {return new info2(true, 0);}info2 leftInfo = process2(head.left);info2 rightInfo = process2(head.right);boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;int height = Math.max(leftInfo.height, rightInfo.height) + 1;return new info2(isFull, height);}
15. 求二叉树中最大的二叉搜索子树的大小
给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的大小
public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static class info {public int maxBSTSubtreeSize;// 最大搜索子树大小public int allSize;// 所有子节点数量public int max;public int min;public info(int maxBSTSubtreeSize, int allSize, int max, int min) {this.maxBSTSubtreeSize = maxBSTSubtreeSize;this.allSize = allSize;this.max = max;this.min = min;}}public static info process(Node head) {if (head == null) {return null;}info leftInfo = process(head.left);info rightInfo = process(head.right);int max = head.value; // 假设为自身int min = head.value;int allSize = 1; // 所有字节节数 算上自身if (leftInfo != null) {max = Math.max(max, leftInfo.max);min = Math.min(min, leftInfo.min);allSize += leftInfo.allSize; // 加上左树的子节点数}if (rightInfo != null) {max = Math.max(max, rightInfo.max);min = Math.min(min, rightInfo.min);allSize += rightInfo.allSize; //加上右树的字节点数}int p1 = -1;if (leftInfo != null) {p1 = leftInfo.maxBSTSubtreeSize;}int p2 = -1;if (rightInfo != null) {p2 = rightInfo.maxBSTSubtreeSize;}int p3 = -1;//判断最大子树大小 与 所有节点数 是否相等(即没有断层)boolean leftBst = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize);boolean rightBst = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize);if (leftBst && rightBst) {// 验证当前节点是否满足二叉树规则 左孩子比自身小 右孩子比自身大boolean leftMaxLeesX = leftInfo == null ? true : (leftInfo.max < head.value);boolean rightMinMoreX = rightInfo == null ? true : (head.value < rightInfo.min);if (leftMaxLeesX && rightMinMoreX) {//如果最大搜索子树大小没断层 并且满足搜索二叉树规则 进行递加int leftSize = leftInfo == null ? 0 : leftInfo.allSize;int rightSize = rightInfo == null ? 0 : rightInfo.allSize;p3 = leftSize + rightSize + 1;}}return new info(Math.max(p1, Math.max(p2, p3)), allSize, max, min);}public static int maxSubBSTSize2(Node head) {if(head == null) {return 0;}return process(head).maxBSTSubtreeSize;}
16. 递归套路版判断是否为完全二叉树
上面我们使用队列按层遍历(bfs)来判断是否为完全二叉树,我们可以改为递归版本使用递归套路
17. 求二叉树最大的二叉搜索树的头节点
给定一棵二叉树的头节点head,返回这颗二叉树中最大的二叉搜索子树的头节点
18. 求二叉树上a和b两节点的最低公共祖先
给定一棵二叉树的头节点head,和另外两个节点a和b,返回a和b的最低公共祖先
public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value = data;}}public static class info {boolean findA;boolean findB;Node ans;public info(boolean findA, boolean findB, Node ans) {this.findA = findA;this.findB = findB;this.ans = ans;}}public static Node lowestAncestor2(Node head, Node a, Node b) {if (head == null) {return null;}return process(head, a, b).ans;}private static info process(Node head, Node a, Node b) {if (head == null) {return new info(false, false, null); // 空节点 返回info信息}info leftInfo = process(head.left, a, b); // 递归下去info rightInfo = process(head.right, a, b);boolean findA = (head == a) || leftInfo.findA || rightInfo.findA; // 如果a/b为自身 或者 左右info之前发现过a/b为trueboolean findB = (head == b) || leftInfo.findB || rightInfo.findB;Node ans = null; // 先初始化if (findA && findB) { // a和b都标记找到if (leftInfo.ans != null) { // 之前leftInfo有标记过祖先,继承之前的祖先,因为题目要求是最低祖先(最先找到的祖先)ans = leftInfo.ans;} else if (rightInfo.ans != null) { // right同理ans = rightInfo.ans;} else {ans = head; // 之前都没有标记过祖先 即当前节点为最先发现的祖先}}return new info(findA, findB, ans); // 返回info信息}
19. 派对的最大快乐值
公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树
树的头节点是公司唯一的老板,除老板之外的每个员工都有唯一的直接上级
叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外每个员工都有一个或多个直接下级
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:
如果某个员工来了,那么这个员工的所有直接下级都不能来
派对的整体快乐值是所有到场员工快乐值的累加
你的目标是让派对的整体快乐值尽量大
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。
public static class Employee {public int happy;public List<Employee> nexts;public Employee(int h) {happy = h;nexts = new ArrayList<>();}}public static class info {int no; // 当前节点不来的情况int yes; // 当前节点来的情况public info(int no, int yes) {super();this.no = no;this.yes = yes;}}public static int maxHappy2(Employee head) {info allInfo = process(head);return Math.max(allInfo.no, allInfo.yes);}private static info process(Employee head) {if (head == null) {return new info(0, 0);}int no = 0;// 不来的情况int yes = head.happy; // 来的情况for (Employee employee : head.nexts) {info nextInfo = process(employee);no += Math.max(nextInfo.no, nextInfo.yes);// 子节点可来可不来yes += nextInfo.no; // 当前节点来了 他的下属子节点必须得不来}return new info(no, yes);}
20. 100. 相同的树
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if (p == null ^ q == null) { //有一个为空 另外一个不为空 返回return false;}if (p == null && q == null) { //两者都为空return true;}//两者值相等 && 左树相等 && 右树相等return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);}}
21. 101. 对称二叉树
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val = val; }TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public boolean isSymmetric(TreeNode root) {return isSameTree(root,root);//自己与自己比较}public boolean isSameTree(TreeNode p, TreeNode q) {if (p == null ^ q == null) { //有一个为空 另外一个不为空 返回return false;}if (p == null && q == null) { //两者都为空return true;}//两者值相等 && 左树与`右树相等 && 右树与`左树相等return p.val == q.val && isSameTree(p.left, q.right) && isSameTree(p.right, q.left);}}
22. 104. 二叉树的最大深度
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public int maxDepth(TreeNode root) {if (root == null) {return 0;}return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;}}
23. 105. 从前序与中序遍历序列构造二叉树
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public TreeNode buildTree(int[] preorder, int[] inorder) {if (preorder == null || inorder == null || preorder.length != inorder.length) {return null;}Map<Integer, Integer> IndexMap = new HashMap<>();for (int i = 0; i < inorder.length; i++) {IndexMap.put(inorder[i], i);}return f(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1,IndexMap);}private TreeNode f(int[] preorder, int l1, int r1, int[] inorder, int l2, int r2,Map<Integer, Integer> IndexMap) {if (l1 > r1) { //return null;}TreeNode head = new TreeNode(preorder[l1]); // 头节点if (l1 == r1) { // 只有一个元素时return head;}int index = IndexMap.get(preorder[l1]); // 查找当前头节点在中序遍历数组的位置// 左树构建 l1+1到 l1 + index - l2 为左树的先序遍历范围 l2到index-1为中序遍历范围head.left = f(preorder, l1 + 1, l1 + index - l2, inorder, l2, index - 1,IndexMap); // 注意左树和右树构建采用的遍历范围均相同长度// 右树构建 l1 + index - l2(即左树的结束范围) +1 到 r1为右树先序遍历范围 index+1到r2为右树中序遍历的范围head.right = f(preorder, l1 + index - l2 + 1, r1, inorder, index + 1, r2,IndexMap);return head;}}
24. 107. 二叉树的层序遍历 II
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {List<List<Integer>> ans = new ArrayList<>();if (root == null) { // 根节点为空直接返回空列表return ans;}LinkedList<TreeNode> queue = new LinkedList<>();queue.add(root); // 将头节点加入队列中while (!queue.isEmpty()) {int size = queue.size(); // 获取队列当前长度List<Integer> tans = new LinkedList<>();for (int i = 0; i < size; i++) {TreeNode temp = queue.poll();tans.add(temp.val); // 获取头节点的值 放入局部队列中if (temp.left != null) {queue.add(temp.left); // 左节点非空 加入到队列中}if (temp.right != null) {queue.add(temp.right); // 右节点非空 加入队列中}}ans.add(0, tans); // 将上次队列缓存头节点清空后的节点值加入到结果集合中}return ans;}}
25. 110. 平衡二叉树
平衡二叉树:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public boolean isBalanced(TreeNode root) {return process(root).isBalanced; //直接返回root节点的信息}public class info {public boolean isBalanced; // 是否为平衡二叉树public int height; // 高度public info(boolean isBalanced, int height) {this.isBalanced = isBalanced;this.height = height;}}public info process(TreeNode root) {if (root == null) { // 空节点return new info(true, 0);}info leftInfo = process(root.left); // 左树信息info rightInfo = process(root.right); // 右树信息int height = Math.max(leftInfo.height, rightInfo.height) + 1; // 当前节点高度为 左和右树最大值 +1boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced&& Math.abs(leftInfo.height - rightInfo.height) <= 1; //左树和右树为平衡二叉树 并且 左右树高度差不大于1return new info(isBalanced, height);}}
26. 98. 验证二叉搜索树
搜索二叉树:左树的值比根节点的值小,右树的值比根节点的值大
一颗搜索二叉树中序遍历是递增顺序
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public boolean isValidBST(TreeNode root) {return isBST(root).isBst;}public class info{boolean isBst;int max;int min;public info(boolean isBst, int max, int min) {this.isBst = isBst;this.max = max;this.min = min;}}public info isBST(TreeNode root) {if(root == null) { //头节点为空则返回空return null;}info leftinfo = isBST(root.left); //递归进入左树info rightinfo = isBST(root.right); //递归进入右树int max = root.val; //假设最大值为自身int min = root.val;//假设最小值为自身if(leftinfo != null) { //左树非空max = Math.max(max, leftinfo.max); //提取左树信息min = Math.min(min, leftinfo.min);}if(rightinfo != null) {max = Math.max(max, rightinfo.max); //提取右树信息min = Math.min(min, rightinfo.min);}boolean bst = true; //先假设为搜索二叉树if(leftinfo !=null && !leftinfo.isBst) { //如果左树不满足则 头节点标记为不是二叉树bst =false;}if(rightinfo !=null && !rightinfo.isBst) { //如果右树不满足则 头节点标记为不是二叉树bst =false;}boolean lefiBst = leftinfo == null ? true : (leftinfo.max < root.val); //如果左树信息为空返回ture 否则左树最大值必须小于当前节点值boolean rightBst = rightinfo == null ? true : (rightinfo.min > root.val);//如果右树信息为空返回ture 否则右树最小值必须大于当前节点值if(!lefiBst || !rightBst) { //不满足搜索二叉树规则bst = false;}return new info(bst, max, min);}}
27. 112. 路径总和
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public boolean isSum = false;public boolean hasPathSum(TreeNode root, int targetSum) {if(root ==null) { //根节点为空 直接返回falsereturn false;}tree(root, 0, targetSum);return isSum;}public void tree(TreeNode root, int preSum, int sum) {if (root.left == null && root.right == null) { //当前头节点左右子节点都为空if (preSum + root.val == sum) { //当前头节点值与上次sum 为target则找到isSum = true;}return;}preSum += root.val; //累加当前sumif (root.left != null) { //左节点有叶节点 递归进去tree(root.left, preSum, sum);}if (root.right != null) { //右节点有叶节点 递归进去tree(root.right, preSum, sum);}}}
28. 113. 路径总和 II
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}}class Solution {public List<List<Integer>> pathSum(TreeNode root, int targetSum) {List<List<Integer>> ans = new ArrayList<>();if (root == null) {return ans;}ArrayList<Integer> path = new ArrayList<>();process(root, path, 0, targetSum, ans);return ans;}public void process(TreeNode root, ArrayList<Integer> path, int preSum, int sum, List<List<Integer>> ans) {if (root.left == null & root.right == null) {if (root.val + preSum == sum) {path.add(root.val); // 添加当前节点路径// List<Integer> newPath = copy(path); // 拷贝List<Integer> newPath = (List<Integer>) path.clone(); // 本质上是浅拷贝 但里面存储的是值 所以复制了里面值 如里面存储的是引用不应用深拷贝ans.add(newPath); // 添加到结果集合中path.remove(path.size() - 1); // 删除当前路径}}preSum += root.val; // 累加 无需回溯 因为是值传递 不是引用传递path.add(root.val); // 记录节点if (root.left != null) { // 递归process(root.left, path, preSum, sum, ans);}if (root.right != null) { // 递归process(root.right, path, preSum, sum, ans);}path.remove(path.size() - 1); // 恢复现场}}
