【题解】—— 每日一道题目栏
上接:【题解】—— LeetCode一周小结7
19.N 叉树的后序遍历
题目链接:590. N 叉树的后序遍历
给定一个 n 叉树的根节点 root ,返回 其节点值的 后序遍历 。
n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。
示例 1:
输入:root = [1,null,3,2,4,null,5,6]
输出:[5,6,3,2,4,1]
示例 2:
输入:root =
[1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]输出:[2,6,14,11,7,3,12,8,4,13,9,10,5,1]
提示:
节点总数在范围 [0, 104] 内
0 <= Node.val <= 104
n 叉树的高度小于或等于 1000
题解:
方法:递归
/*
// Definition for a Node.
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;}
};
*/class Solution {List<Integer> ans = new ArrayList<>();public List<Integer> postorder(Node root) {dfs(root);return ans;}void dfs(Node root) {if (root == null) return;for (Node node : root.children) dfs(node);ans.add(root.val);}
}
方法:非递归
/*
// Definition for a Node.
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;}
};
*/class Solution {public List<Integer> postorder(Node root) {List<Integer> ans = new ArrayList<>();Deque<Object[]> d = new ArrayDeque<>();d.addLast(new Object[]{0, root});while (!d.isEmpty()) {Object[] poll = d.pollLast();Integer cnt = (Integer)poll[0]; Node t = (Node)poll[1];if (t == null) continue;if (cnt == t.children.size()) ans.add(t.val);if (cnt < t.children.size()) {d.addLast(new Object[]{cnt + 1, t});d.addLast(new Object[]{0, t.children.get(cnt)}); }}return ans;}
}
20.从前序与中序遍历序列构造二叉树
题目链接:105. 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列
题解:
方法:递归
变量 pre 保存当前要构造的树的 root
变量 in 保存 inorder 数组中可以成为 root 的数字们的开头那个
对于当前要构造的树,有一个停止点 stop ,inorder 数组中第 in 项到第 stop 项是要构造的树的节点值们
每次递归调用,都会确定出一个停止点,它告诉了子
调用在哪里停止,把自己的根节点值作为左子树调用的停止点,自己的(父调用给下来的)停止点作为右子树的停止点
/*** Definition for a binary tree node.* 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 {private int in = 0;private int pre = 0;public TreeNode buildTree(int[] preorder, int[] inorder) {return build(preorder, inorder, Integer.MIN_VALUE);}private TreeNode build(int[] preorder, int[] inorder, int stop) {if (pre == preorder.length) return null; // pre走到preorder末尾if (inorder[in] == stop) { // in指针走到了停止点in++; // stop点废弃了,in推进一位return null;}TreeNode node = new TreeNode(preorder[pre++]);node.left = build(preorder, inorder, node.val);// 左子树的停止点是当前的根节点的值node.right = build(preorder, inorder, stop);// 右子树的停止点是当前树的停止点return node; }
}
21.从中序与后序遍历序列构造二叉树
题目链接:106. 从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历
题解:
方法:递归 分治
/*** Definition for a binary tree node.* 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 {HashMap<Integer,Integer> memo = new HashMap<>();int[] post;public TreeNode buildTree(int[] inorder, int[] postorder) {for(int i = 0;i < inorder.length; i++) memo.put(inorder[i], i);post = postorder;TreeNode root = buildTree(0, inorder.length - 1, 0, post.length - 1);return root;}public TreeNode buildTree(int is, int ie, int ps, int pe) {if(ie < is || pe < ps) return null;int root = post[pe];int ri = memo.get(root);TreeNode node = new TreeNode(root);node.left = buildTree(is, ri - 1, ps, ps + ri - is - 1);node.right = buildTree(ri + 1, ie, ps + ri - is, pe - 1);return node;}
}
22.根据前序和后序遍历构造二叉树
题目链接:889. 根据前序和后序遍历构造二叉树
给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。
示例 1:
输入:preorder = [1,2,4,5,3,6,7], postorder = [4,5,2,6,7,3,1]
输出:[1,2,3,4,5,6,7]
示例 2:
输入: preorder = [1], postorder = [1]
输出: [1]
提示:
1 <= preorder.length <= 30
1 <= preorder[i] <= preorder.length
preorder 中所有值都 不同
postorder.length == preorder.length
1 <= postorder[i] <= postorder.length
postorder 中所有值都 不同
保证 preorder 和 postorder 是同一棵二叉树的前序遍历和后序遍历
题解:
方法:递归
前序遍历:按照「根-左子树-右子树」的顺序遍历二叉树。
后序遍历:按照「左子树-右子树-根」的顺序遍历二叉树。
/*** Definition for a binary tree node.* 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 constructFromPrePost(int[] preorder, int[] postorder) {int n = preorder.length;if (n == 0) { // 空节点return null;}if (n == 1) { // 叶子节点return new TreeNode(preorder[0]);}int leftSize = indexOf(postorder, preorder[1]) + 1; // 左子树的大小int[] pre1 = Arrays.copyOfRange(preorder, 1, 1 + leftSize);int[] pre2 = Arrays.copyOfRange(preorder, 1 + leftSize, n);int[] post1 = Arrays.copyOfRange(postorder, 0, leftSize);int[] post2 = Arrays.copyOfRange(postorder, leftSize, n - 1);TreeNode left = constructFromPrePost(pre1, post1);TreeNode right = constructFromPrePost(pre2, post2);return new TreeNode(preorder[0], left, right);}// 返回 x 在 a 中的下标,保证 x 一定在 a 中private int indexOf(int[] a, int x) {for (int i = 0; ; i++) {if (a[i] == x) {return i;}}}
}
23. 二叉树中的第 K 大层和
题目链接:2583. 二叉树中的第 K 大层和
给你一棵二叉树的根节点 root 和一个正整数 k 。
树中的 层和 是指 同一层 上节点值的总和。
返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1 。
注意,如果两个节点与根节点的距离相同,则认为它们在同一层。
示例 1:
输入:root = [5,8,9,2,1,3,7,4,6], k = 2
输出:13
解释:树中每一层的层和分别是:
- Level 1: 5
- Level 2: 8 + 9 = 17
- Level 3: 2 + 1 + 3 + 7 = 13
- Level 4: 4 + 6 = 10
第 2 大的层和等于 13 。
示例 2:
输入:root = [1,2,null,3], k = 1
输出:3
解释:最大的层和是 3 。
提示:
树中的节点数为 n
2 <= n <= 105
1 <= Node.val <= 106
1 <= k <= n
题解:
方法:BFS+排序
首先,利用 BFS,可以得到二叉树每一层的节点值之和。BFS 的同时,把每一层的节点值之和保存到一个列表 a 中,把 a 排序后就可以得到第 k 大。此外,也可以用快速选择得到第 k 大。
如果 k 大于 a 的长度,返回 −1。
/*** Definition for a binary tree node.* 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 long kthLargestLevelSum(TreeNode root, int k) {List<Long> a = new ArrayList<>();List<TreeNode> q = List.of(root);while (!q.isEmpty()) {long sum = 0;List<TreeNode> tmp = q;q = new ArrayList<>();for (TreeNode node : tmp) {sum += node.val;if (node.left != null) q.add(node.left);if (node.right != null) q.add(node.right);}a.add(sum);}int n = a.size();if (k > n) {return -1;}Collections.sort(a);return a.get(n - k);}
}
24.二叉搜索树最近节点查询
题目链接:2476. 二叉搜索树最近节点查询
给你一个 二叉搜索树 的根节点 root ,和一个由正整数组成、长度为 n 的数组 queries 。
请你找出一个长度为 n 的 二维 答案数组 answer ,其中 answer[i] = [mini, maxi] :
- mini 是树中小于等于 queries[i] 的 最大值 。如果不存在这样的值,则使用 -1 代替。
- maxi 是树中大于等于 queries[i] 的 最小值 。如果不存在这样的值,则使用 -1 代替。
返回数组 answer 。
示例 1 :
输入:root = [6,2,13,1,4,9,15,null,null,null,null,null,null,14], queries
= [2,5,16]输出:[[2,2],[4,6],[15,-1]]
解释:按下面的描述找出并返回查询的答案:
- 树中小于等于 2 的最大值是 2 ,且大于等于 2 的最小值也是 2 。所以第一个查询的答案是 [2,2] 。
- 树中小于等于 5 的最大值是 4 ,且大于等于 5 的最小值是 6 。所以第二个查询的答案是 [4,6] 。
- 树中小于等于 16 的最大值是 15 ,且大于等于 16 的最小值不存在。所以第三个查询的答案是 [15,-1] 。
示例 2 :
输入:root = [4,null,9], queries = [3]
输出:[[-1,4]]
解释:树中不存在小于等于 3 的最大值,且大于等于 3 的最小值是 4 。所以查询的答案是 [-1,4] 。
提示:
树中节点的数目在范围 [2, 105] 内
1 <= Node.val <= 106
n == queries.length
1 <= n <= 105
1 <= queries[i] <= 106
题解:
方法:中序遍历+二分查找
题目没说二叉搜索树是平衡的,最坏情况下这棵树是一条链,此时单次询问的复杂度是 O(n) 的,其中 n 为二叉搜索树的节点个数。
设 j 是大于等于 q=queriesi的第一个数的下标,如果不存在则 j=n。
-
对于 maxi:
- 如果 j<n,那么 maxi=a[j]。
- 否则 maxi=−1。
-
对于 mini:
- 如果 j<n 且 a[j]=q,那么 mini=a[j]。
- 否则如果 j>0,那么 mini=a[j−1]。
- 否则 mini=−1。
/*** Definition for a binary tree node.* 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>> closestNodes(TreeNode root, List<Integer> queries) {List<Integer> arr = new ArrayList<>();dfs(root, arr);int n = arr.size();int[] a = new int[n];for (int i = 0; i < n; i++) {a[i] = arr.get(i); // 转成数组,效率更高}List<List<Integer>> ans = new ArrayList<>(queries.size()); // 预分配空间for (int q : queries) {int j = lowerBound(a, q);int mx = j == n ? -1 : a[j];if (j == n || a[j] != q) { // a[j]>q, a[j-1]<qj--;}int mn = j < 0 ? -1 : a[j];ans.add(List.of(mn, mx));}return ans;}private void dfs(TreeNode node, List<Integer> a) {if (node == null) {return;}dfs(node.left, a);a.add(node.val);dfs(node.right, a);}// 见 https://www.bilibili.com/video/BV1AP41137w7/private int lowerBound(int[] a, int target) {int left = -1, right = a.length; // 开区间 (left, right)while (left + 1 < right) { // 区间不为空int mid = (left + right) >>> 1; // 比 /2 快if (a[mid] >= target) {right = mid; // 范围缩小到 (left, mid)} else {left = mid; // 范围缩小到 (mid, right)}}return right;}
}
25.二叉搜索树的最近公共祖先
题目链接:235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。
题解:
方法:递归
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {int x = root.val;if (p.val < x && q.val < x) { // p 和 q 都在左子树return lowestCommonAncestor(root.left, p, q);}if (p.val > x && q.val > x) { // p 和 q 都在右子树return lowestCommonAncestor(root.right, p, q);}return root; // 其它}
}
下接:【题解】—— LeetCode一周小结9