题目来源:https://leetcode-cn.com/problems/minimum-absolute-sum-difference/
大致题意:
给定两个长度相同的数组,在最多改动(将某个位置元素用另一位置元素替代)数组一其中一个元素的情况下,求得两数组相同位置元素的所有绝对值和的最小值。
思路
改动前的绝对值和:
- | nums1[i] - nums2[i] | 的所有和
改动后的绝对值和:
即某个位置的元素被另一位置元素替代,改动后该位置的绝对值为
- | nums1[j] - nums2[i] |,j即为替换元素位置,i为被替换元素位置
改动前后的差值为:
- | nums1[i] - nums2[i] | - | nums1[j] - nums2[i] |
若想找到绝对值和的最小值,那么即为上述差值的最大值。因为差值越大,代表原绝对值减去的值也就越大,那么绝对值和自然也就是最小的。
那么解题思路就可为:求出原绝对值和 Sum + 求出最大的上述差值
然后使用 Sum - 最大差值,即为所求答案
排序加二分查找
那么在刚刚的分析结果下,可以先对 nums1 数组进行排序,然后在 | nums1[i] - nums2[i] |已知的情况下,求出最小的 | nums1[j] - nums2[i] |,也就是在排序后的 nums1 数组中二分查找 nums2[i] 最相近的值。
而这个最相近的值,有三种可能性:
- 和 nums2[i] 相同
- 比 nums2[i] 小
- 比 nums2[i] 大
而二分的思路,若有相同,返回的索引即为该相同元素位置索引;
public int binarySearch(int[] nums, int num) {
int low = 0;
int high = nums.length;
while (low < high) {
int mid = (low + high) / 2;
if (nums[mid] < num) { // 若当前位置小于目标值,二分查找右半段
low = mid + 1;
}
else {
high = mid;
}
}
return low;
}
若不相同,那么返回的索引即为大一级元素的索引,于是返回索引即为大的位置索引,返回索引 - 1 即为小的元素索引。
于是可以对比一下两个元素哪个是和nums2[i]最相近的值。
代码如下:
public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
int maxSum = 0;
final int mod = 1000000007;
int[] sortedNums = new int[nums1.length];
System.arraycopy(nums1, 0, sortedNums, 0, nums1.length); // 数组内存拷贝
Arrays.sort(sortedNums); // 排序
int maxDiff = 0;
for (int i = 0; i < nums1.length; i++) {
int diff = Math.abs(nums1[i] - nums2[i]); // 当前差值
maxSum = (maxSum + diff) % mod;
int idx = binarySearch(sortedNums, nums2[i]); // 在排序后的数组中查找和nums2[i]最相近的值
if (idx < nums1.length) { // 如果索引位置未超出数组长度
maxDiff = Math.max(maxDiff, diff - Math.abs(sortedNums[idx] - nums2[i]));
}
if (idx > 0) { // 如果索引位置不为首元素
maxDiff = Math.max(maxDiff, diff - Math.abs(nums2[i] - sortedNums[idx-1]));
}
}
return (maxSum - maxDiff + mod) % mod;
}