1337. 方阵中战斗力最弱的 k 行
难度简单6收藏分享切换为英文关注反馈
给你一个大小为 m * n
的方阵 mat
,方阵由若干军人和平民组成,分别用 0 和 1 表示。
请你返回方阵中战斗力最弱的 k
行的索引,按从最弱到最强排序。
如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。
军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。
示例 1:
输入:mat =
[[1,1,0,0,0],
[1,1,1,1,0],
[1,0,0,0,0],
[1,1,0,0,0],
[1,1,1,1,1]],
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2
行 1 -> 4
行 2 -> 1
行 3 -> 2
行 4 -> 5
从最弱到最强对这些行排序后得到 [2,0,3,1,4]
本题的难点在于索引绑定。涉及到有关Java中实现Map按值排序的相关技巧。
public int[] kWeakestRows(int[][] mat, int k) {
Map<Integer, Integer> map = new HashMap<>(); //问题:如何建立联系
// List<Integer> list = new ArrayList<>();
for (int i = 0; i < mat.length; i++){
int count = 0;
for (int j = 0; j < mat[0].length; j++) {
if (mat[i][j] == 1) {
count += 1;
}
}
map.put(i, count);
}
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
int[] res = new int[k];
for (int i = 0; i < k; i++) {
Map.Entry<Integer, Integer> entry = list.get(i);
res[i] = entry.getKey();
}
return res;
}
1338. 数组大小减半
给你一个整数数组 arr。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。
返回 至少 能删除数组中的一半整数的整数集合的最小大小。
示例 1:
输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。
与上一题如出一辙,都是Map按值排序。
public int minSetSize(int[] arr) {
int len = arr.length;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
if (map.containsKey(arr[i])) {
int count = map.get(arr[i]);
map.put(arr[i], count + 1);
} else {
map.put(arr[i], 1);
}
}
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
int result = 0;
int sum = 0;
for (int i = list.size() - 1; i >= 0; i--) {
Map.Entry<Integer, Integer> entry = list.get(i);
sum += entry.getValue();
if (sum < (len / 2)) {
result++;
} else {
return result + 1;
}
}
return result;
}
1339. 分裂二叉树的最大乘积
给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。
由于答案可能会很大,请你将结果对 10^9 + 7 取模后再返回。
示例 1:
输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)
分析:
首先要理解分裂,当一棵树分裂以后,其中一个树(1树)的根节点为原本树的根节点,另一个树(2树)的根节点为断裂边的一端节点。那么枚举每一根边断裂的情况,可以发现 任何一个节点(除根节点)均可以作为2树的根节点。
- 题目就转化为遍历每一个子节点,计算以该节点为根节点的树子节点之和。
- 分别求两棵子树的和显然很难做到,但我们可以很方便的其中一个子树的和。
- 用整棵树的总和减去求出的子树和,就得到了另一棵子树和。
- 枚举所有子树,对乘积取最大。
public class Solution {
double ans = Double.MIN_VALUE;
double allSum;
double nodeSum;
public int maxProduct(TreeNode root) {
allSum = sum(root);
dfs(root);
return (int) (ans % (int) (1e9 + 7));
}
public double sum(TreeNode node) {
if (node == null) return 0;
return node.val + sum(node.left) + sum(node.right);
}
public double dfs(TreeNode node) {
if (node == null) return 0;
nodeSum = node.val + dfs(node.left) + dfs(node.right);
ans = Math.max(ans, (allSum - nodeSum) * nodeSum); //另一棵子树 = 和 - 深搜子树
return nodeSum;
}
}
1340. 跳跃游戏 V
给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到:
- i + x ,其中 i + x < arr.length 且 0 < x <= d 。
- i - x ,其中 i - x >= 0 且 0 < x <= d 。
除此以外,你从下标 i 跳到下标 j 需要满足:arr[i] > arr[j] 且 arr[i] > arr[k] ,其中下标 k 是所有 i 到 j 之间的数字(更正式的,min(i, j) < k < max(i, j))。
你可以选择数组的任意下标开始跳跃。请你返回你 最多 可以访问多少个下标。
请注意,任何时刻你都不能跳到数组的外面。
示例 1:
输入:arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
输出:4
解释:你可以从下标 10 出发,然后如上图依次经过 10 --> 8 --> 6 --> 7 。
注意,如果你从下标 6 开始,你只能跳到下标 7 处。你不能跳到下标 5 处因为 13 > 9 。你也不能跳到下标 4 处,因为下标 5 在下标 4 和 6 之间且 13 > 9 。
类似的,你不能从下标 3 处跳到下标 2 或者下标 1 处。
class Solution {
private Integer[] cache;
public int maxJumps(int[] arr, int d) {
cache = new Integer[arr.length];
int res = 0;
for (int i = 0; i < arr.length; i++) {
res = Math.max(res, dfs(arr, i, d));
}
return res;
}
private int dfs(int[] arr, int cur, int d) {
if (cache[cur] != null) { //判空,必须为包装类
return cache[cur];
}
// 确定左右边界
int left = Math.max(0, cur - d);
int right = Math.min(arr.length - 1, cur + d);
int step = 0;
for (int direction = -1; direction <= 1; direction += 2) { //方向:向左跳/向右跳
for (int i = cur + direction; i <= right && i >= left; i += direction) {
if (arr[i] >= arr[cur]) {
break;
}
step = Math.max(step, dfs(arr, i, d));
}
}
return cache[cur] = step + 1;
}
}