100149. Find Missing and Repeated Values
给你一个下标从 0 开始的二维整数矩阵 grid,大小为 n * n ,其中的值在 [1, n2] 范围内。除了 a 出现 两次,b 缺失 之外,每个整数都 恰好出现一次 。
任务是找出重复的数字a 和缺失的数字 b 。
返回一个下标从 0 开始、长度为 2 的整数数组 ans ,其中 ans[0] 等于 a ,ans[1] 等于 b 。
测试样例:
输入:grid = [[1,3],[2,2]]
输出:[2,4]
解释:数字 2 重复,数字 4 缺失,所以答案是 [2,4] 。
解答:这题可以利用位运算符 + 原地正负位运算,用O(1)的空间完成计算。但是easy题,就直接用HashMap暴力算了。
class Solution {
public int[] findMissingAndRepeatedValues(int[][] grid) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int[] r : grid) {
for (int n : r) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
}
int[] res = new int[2];
for (int i = 1; i <= grid.length * grid.length; ++i) {
int tmp = map.getOrDefault(i, 0);
if (tmp == 2) {
res[0] = i;
} else if (tmp == 0) {
res[1] = i;
}
}
return res;
}
}
100161. Divide Array Into Arrays With Max Difference
给你一个长度为 n 的整数数组 nums,以及一个正整数 k 。
将这个数组划分为一个或多个长度为 3 的子数组,并满足以下条件:
- nums 中的 每个 元素都必须 恰好 存在于某个子数组中。
- 子数组中 任意 两个元素的差必须小于或等于 k 。
返回一个 二维数组 ,包含所有的子数组。如果不可能满足条件,就返回一个空数组。如果有多个答案,返回 任意一个 即可。
测试样例:
输入:nums = [1,3,4,8,7,9,3,5,1], k = 2
输出:[[1,1,3],[3,4,5],[7,8,9]]
解释:可以将数组划分为以下子数组:[1,1,3],[3,4,5] 和 [7,8,9] 。每个子数组中任意两个元素的差都小于或等于 2 。注意,元素的顺序并不重要。
解答:将数组排序之后,往最终结果中填充。因为已经排序了,这个条件:子数组中 任意 两个元素的差必须小于或等于 k 。应该是最容易满足的,如果无法达到要求,返回空数组。
class Solution {
private static final int[][] fail = new int[0][];
public int[][] divideArray(int[] nums, int k) {
Arrays.sort(nums);
int[][] res = new int[nums.length / 3][3];
for (int i = 0; i < nums.length; ++i) {
int x = i / 3, y = i % 3;
res[x][y] = nums[i];
if (res[x][y] - res[x][0] > k) {
return fail;
}
}
return res;
}
}
100151. Minimum Cost to Make Array Equalindromic
给你一个长度为 n 下标从 0 开始的整数数组 nums 。
你可以对 nums 执行特殊操作 任意次 (也可以 0 次)。每一次特殊操作中,你需要 按顺序 执行以下步骤:
- 从范围 [0, n - 1] 里选择一个下标 i 和一个 正 整数 x 。
- 将 |nums[i] - x| 添加到总代价里。
- 将 nums[i] 变为 x 。
如果一个正整数正着读和反着读都相同,那么我们称这个数是 回文数 。比方说,121 ,2552 和 65756 都是回文数,但是 24 ,46 ,235 都不是回文数。
如果一个数组中的所有元素都等于一个整数 y ,且 y 是一个小于 10^9 的 回文数 ,那么我们称这个数组是一个 等数数组 。
请你返回一个整数,表示执行任意次特殊操作后使 nums 成为 等数数组 的 最小 总代价。
测试样例:
输入:nums = [1,2,3,4,5]
输出:6
解释:我们可以将数组中所有元素变为回文数 3 得到等数数组,数组变成 [3,3,3,3,3] 需要执行 4 次特殊操作,代价为 |1 - 3| + |2 - 3| + |4 - 3| + |5 - 3| = 6 。
将所有元素变为其他回文数的总代价都大于 6 。
解答:绝对值求和,可以很快速的知道,取中位数是最佳结果。接下来的问题就是寻找中位数附近的回文数,并且枚举出最优解。寻找附近的回文数可以把中位数从当中切分,然后把这半个数分别进行-1, 0, +1操作,并拼接回去。这样形成的数一定是最接近的回文数。然后暴力枚举这三种情况的最优值。
class Solution {
private static final int[] oper = {0,-1,1};
public long minimumCost(int[] nums) {
Arrays.sort(nums);
int mid = nums[nums.length / 2];
long min = Long.MAX_VALUE;
for (long t : findPalindromic(mid)) {
long cur = 0;
for (int n : nums) {
cur += Math.abs(t - n);
}
min = Math.min(min, cur);
}
return min;
}
private long[] findPalindromic(int n) {
String tmp = String.valueOf(n);
String split = tmp.substring(0, tmp.length() / 2 + (tmp.length() % 2));
int val = Integer.parseInt(split);
long[] res = new long[3];
for (int i = 0; i < oper.length; ++i) {
String v = String.valueOf(val + oper[i]);
if (v.length() < split.length() || (oper[i] == -1 && val == 1 && tmp.length() == 2)) {
res[i] = res[0] - 2;
} else if (v.length() > split.length()) {
res[i] = res[0] + 2;
} else if (tmp.length() % 2 == 0) {
StringBuilder sb = new StringBuilder(v);
res[i] = Long.parseLong(v + sb.reverse().toString());
} else {
StringBuilder sb = new StringBuilder(v);
res[i] = Long.parseLong(v.substring(0, v.length() - 1) + sb.reverse().toString());
}
}
return res;
}
}
100123. Apply Operations to Maximize Frequency Score
给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
你可以对数组执行 至多 k 次操作:
- 从数组中选择一个下标 i ,将 nums[i] 增加 或者 减少 1 。
最终数组的频率分数定义为数组中众数的 频率 。
请你返回你可以得到的 最大 频率分数。
众数指的是数组中出现次数最多的数。一个元素的频率指的是数组中这个元素的出现次数。
测试样例:
输入:nums = [1,2,6,4], k = 3
输出:3
解释:我们可以对数组执行以下操作:
- 选择 i = 0 ,将 nums[0] 增加 1 。得到数组 [2,2,6,4] 。
- 选择 i = 3 ,将 nums[3] 减少 1 ,得到数组 [2,2,6,3] 。
- 选择 i = 3 ,将 nums[3] 减少 1 ,得到数组 [2,2,6,2] 。
元素 2 是最终数组中的众数,出现了 3 次,所以频率分数为 3 。3 是所有可行方案里的最大频率分数。
解答:今天的题目还真是喜欢用到中位数这个性质。这题还是排序(这样最接近的数就会在相邻位置)。然后利用双指针来寻找最佳范围。
class Solution {
public int maxFrequencyScore(int[] nums, long k) {
Arrays.sort(nums);
long[] prefixSum = new long[nums.length + 1];
for (int i = 0; i < nums.length; ++i) {
prefixSum[i + 1] = prefixSum[i] + nums[i];
}
int st = 0, ed = 0;
int res = 0;
while (st < nums.length) {
while (ed < nums.length && cal(nums, prefixSum, st, ed) <= k) {
++ed;
}
res = Math.max(res, ed - st);
++st;
ed = Math.max(st, ed);
}
return res;
}
private long cal(int[] nums, long[] prefixSum, int st, int ed) {
int mid = (st + ed) / 2;
long midNum = nums[mid];
long diff = midNum * (mid - st + 1) - (prefixSum[mid + 1] - prefixSum[st]);
if (mid + 1 <= ed) {
diff += prefixSum[ed + 1] - prefixSum[mid + 1] - midNum * (ed - mid - 1 + 1);
}
return diff;
}
}