给你两个正整数数组 nums1 和 nums2 ,数组的长度都是 n 。
数组 nums1 和 nums2 的 绝对差值和 定义为所有 |nums1[i] - nums2[i]|(0 <= i < n)的 总和(下标从 0 开始)。
你可以选用 nums1 中的 任意一个 元素来替换 nums1 中的 至多 一个元素,以 最小化 绝对差值和。
在替换数组 nums1 中最多一个元素 之后 ,返回最小绝对差值和。因为答案可能很大,所以需要对 109 + 7 取余 后返回。
|x| 定义为:
如果 x >= 0 ,值为 x ,或者
如果 x <= 0 ,值为 -x示例 1:
输入:nums1 = [1,7,5], nums2 = [2,3,5]
输出:3
解释:有两种可能的最优方案:
- 将第二个元素替换为第一个元素:[1,7,5] => [1,1,5] ,或者
- 将第二个元素替换为第三个元素:[1,7,5] => [1,5,5]
两种方案的绝对差值和都是 |1-2| + (|1-3| 或者 |5-3|) + |5-5| = 3
这题看的第一眼就想用暴力,如果直接暴力抱歉直接给你个超时,不过可以改变下暴力的做法,因为他们只改变了一个数,我们只要原始的和减去Math.abs(nums1[i]-nums2[i])-Math.abs(num1[j]-num2[i]) 只要减去替换后改变的数值,就可以了。之后,只需要找到最小值就行了。本次主要用二分解决,其实二分也是在暴力的基础上,做改进的。
二分查找
题中说替换nums1中至多一个数,来求最小化绝对差值和。既然是这样,我们只需要针对nums2中的每一个数来找num1中最接近num2中的数,最接近的就是绝对差值最小的,也就是我们要替换的。但是num1是无序的,但又不能改变num1的顺序,只能复制num1数组,可以用Arrays.copy或System.arraycopy(Object var0, int var1, Object var2, int var3, int var4)[var0:要复制的数组,var1:要复制的数组起始位置,var2:保存数据的目标数组,var3:目标数组的下标位置,var4:每次复制的长度],然后进行排序,这样的话我们就可以进行排序了,然后我们把nums2中的数作为target在num1中复制的新数组中用二分查找,保存最小绝对值差和(不需要每次都去求循环去求,关于这点上面已经提过),最后就可以求出答案了。
代码:
class Solution {
public int minAbsoluteSumDiff(int[] nums1, int[] nums2) {
final int mod=1000000007;//因为绝对差值和会超过范围需要取余
int []res=Arrays.copyOf(nums1,nums1.length);//res为复制num1后的新数组
Arrays.sort(res);
int sum=0;//
int maxSub=0;
for(int i=0;i<nums1.length;i++){
int sub=Math.abs(nums1[i]-nums2[i]);
sum=(sum+sub)%mod;//求原始绝对差值和
int j=binarySearch(res,nums2[i]);//查找当前nums2在res中 最接近的下标
//由于是求绝对值所以两边都判断
if(j<nums1.length){//大于num2[i]的数
maxSub=Math.max(maxSub,sub-(res[j]-nums2[i]));
}
if(j>0){//小于nums2[i]的数
maxSub=Math.max(maxSub,sub-(nums2[i]-res[j-1]));
}
}
return (sum-maxSub+mod)%mod;//由于可能sum<maxSub
}
public static int binarySearch(int[] res,int target){
int left=0,right=res.length-1;
if(target>res[res.length-1]){
return res.length;
}
while(left<right){
int mid=(right-left)/2+left;
if(res[mid]>=target){
right=mid;
}else{
left=mid+1;
}
}
return right;
}
}