LeetCode刷题之1818. 绝对差值和

给你两个正整数数组 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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值