首页 > 其他分享 >二分查找 | 绝对差值和

二分查找 | 绝对差值和

时间:2024-07-21 17:24:49浏览次数:12  
标签:二分 min sum pos 查找 差值 sorted nums1 nums2

题目:1818. 绝对差值和

给你两个正整数数组 nums1nums2 ,数组的长度都是 n

数组 nums1nums2绝对差值和 定义为所有 |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

示例 2:

输入:nums1 = [2,4,6,8,10], nums2 = [2,4,6,8,10]
输出:0
解释:nums1 和 nums2 相等,所以不用替换元素。绝对差值和为 0

示例 3

输入:nums1 = [1,10,4,4,2,7], nums2 = [9,3,5,1,7,4]
输出:20
解释:将第一个元素替换为第二个元素:[1,10,4,4,2,7] => [10,10,4,4,2,7]
绝对差值和为 |10-9| + |10-3| + |4-5| + |4-1| + |2-7| + |7-4| = 20

提示:

  • n == nums1.length
  • n == nums2.length
  • 1 <= n <= 105
  • 1 <= nums1[i], nums2[i] <= 105

解题思路

  • 题目核心:通过替换 nums1 中的一个元素,使得 nums1nums2 之间的绝对差值和最小化。

  • 二分查找实现步骤:

    1. 计算初始绝对差值和
    2. nums1排序
    3. 遍历nums1并尝试替换每个元素
      • 二分查找找到最接近 nums2[i] 的值
      • 检查 pospos-1 位置的值
    4. 取模并返回结果

暴力解法(超时)

from typing import List

class Solution:
    def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
        MOD = 10**9 + 7
        n = len(nums1)
        
        # 计算初始的绝对差值和
        initial_sum = sum(abs(nums1[i] - nums2[i]) for i in range(n))
        min_sum = initial_sum
        
        # 尝试替换 nums1 中的每个元素
        for i in range(n):
            current_diff = abs(nums1[i] - nums2[i])
            
            # 遍历 nums1 中的每个元素作为替换候选
            for j in range(n):
                if i != j:
                    new_diff = abs(nums1[j] - nums2[i])
                    new_sum = initial_sum - current_diff + new_diff
                    min_sum = min(min_sum, new_sum)
        
        return min_sum % MOD

# 示例测试用例
solution = Solution()
print(solution.minAbsoluteSumDiff([1, 10, 4, 4, 2, 7], [9, 3, 5, 1, 7, 4]))  # 应输出:20

二分查找

from bisect import bisect_left
from typing import List

class Solution:
    def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
        MOD = 10**9 + 7
        n = len(nums1)
        sorted_nums1 = sorted(nums1)
        
        # 初始绝对差值和
        initial_sum = sum(abs(nums1[i] - nums2[i]) for i in range(n))
        min_sum = initial_sum
        
        for i in range(n):
            current_diff = abs(nums1[i] - nums2[i])
            
            # 二分查找找到最接近 nums2[i] 的值
            pos = bisect_left(sorted_nums1, nums2[i])
            
            # 检查 pos 位置的值
            if pos < n:
                new_diff = abs(sorted_nums1[pos] - nums2[i])
                new_sum = initial_sum - current_diff + new_diff
                min_sum = min(min_sum, new_sum)
            
            # 检查 pos-1 位置的值
            if pos > 0:
                new_diff = abs(sorted_nums1[pos - 1] - nums2[i])
                new_sum = initial_sum - current_diff + new_diff
                min_sum = min(min_sum, new_sum)
        
        return min_sum % MOD

反思

  1. 为什么要找到替换为最接近 nums2[i] 的值?

如果我们想让这个差值最小,我们需要让 nums1[i] 尽量接近 nums2[i]。因为:

  • 当两个数接近时,它们的差值 |nums1[i] - nums2[i]| 也会很小。
  • 例如,如果 nums2[i] = 5,而我们有两个候选值 47,显然 47 更接近 5,因此 |4 - 5||7 - 5| 小。
  1. 为什么要使用bisect_left,而不是bisect_right

bisect_left 返回的是数组中第一个大于或等于查找值的位置。bisect_right 返回的是数组中第一个大于查找值的位置。

假设我们有一个排序数组 sorted_nums1 = [1, 2, 4, 4, 7, 10],我们要查找 nums2[i] = 4 在这个数组中的插入点。

  • bisect_left(sorted_nums1, 4) 返回 2,因为 sorted_nums1[2] 是 4,这是第一个大于或等于 4 的位置。
  • bisect_right(sorted_nums1, 4) 返回 4,因为 sorted_nums1[4] 是 7,这是第一个大于 4 的位置。

在这一题目中,我们需要找到最接近 nums2[i] 的值,以尽可能减少绝对差值和。使用 bisect_left 可以帮助我们找到最接近的值,并确保我们不会错过等于 nums2[i] 的值。

  1. 为什么要检查两个位置(pospos-1)?

当我们使用 bisect_left(sorted_nums1, nums2[i]) 时,我们得到的是 nums2[i]sorted_nums1 中的插入位置 pos。这个位置表示在保持数组有序的情况下,nums2[i] 应该插入的位置。

  • pos位置解释:sorted_nums1[pos] 是第一个大于或等于 nums2[i] 的元素。因此,sorted_nums1[pos] 可能是一个接近 nums2[i] 的候选值。
  • pos-1 位置解释:sorted_nums1[pos-1] 是小于 nums2[i] 的最大元素。由于 pos-1 位置的元素也是接近 nums2[i] 的另一个候选值,因此我们也需要检查这个位置。

所以,检查这两个位置的原因为:

  • 全面覆盖可能的候选值
  • 确保找到最优解
  1. pos < npos >0 该如何理解?

当我们使用 bisect_left(sorted_nums1, nums2[i]) 时,得到的 pos 是插入位置,即第一个大于或等于 nums2[i] 的位置。这个位置有可能是数组的末尾,因此我们需要检查 pos < n,确保 pos 在有效范围内。

为什么要检查 pos < n

  • 防止数组越界:如果 pos 恰好等于 n,说明 nums2[i] 大于 sorted_nums1 中的所有元素。在这种情况下,访问 sorted_nums1[pos] 会导致数组越界错误。
  • 确保有效访问:只有在 pos < n 时,我们才能安全地访问 sorted_nums1[pos],因为这时 pos 是一个有效的索引。

类似地,我们还需要检查 pos-1 的位置,以便找到小于 nums2[i] 的最大值。为了确保 pos-1 在有效范围内,我们需要检查 pos > 0

为什么要检查 pos > 0

  • 防止数组越界:如果 pos 恰好等于 0,说明 nums2[i] 小于或等于 sorted_nums1 中的所有元素。在这种情况下,访问 sorted_nums1[pos-1] 会导致数组越界错误。
  • 确保有效访问:只有在 pos > 0 时,我们才能安全地访问 sorted_nums1[pos-1],因为这时 pos-1 是一个有效的索引。

标签:二分,min,sum,pos,查找,差值,sorted,nums1,nums2
From: https://blog.csdn.net/YuvalNoah/article/details/140579538

相关文章

  • 绝对差值减去百分比
    我有一个DataFrame来查找python中两个源之间的绝对差异百分比。但是当我使用下面的代码时,很少有列给出-%(负百分比)我已经检查了显示负百分比数据类型的列在两个源中是否相同。任何人都可以帮助我找出答案为什么?#Definethecolumnsyouwanttoprocesscolum......
  • 如何查找特定单词在哪一行]
    我正在尝试创建一个程序来计算行数,计算特定单词,然后告诉您它在哪一行。现在我做了前两个,但是我不知道如何找到这个词在哪一行。有什么建议么?这是我当前的代码:name=input('Enternameofthetextfile:')+'.txt'file=open(name,'r')myDict={}linenum=0forline......
  • 查找 .txt 文件中给定单词出现的行
    目标是找到给定.txt文件中某个单词出现的行。在我的代码中,我定义了一个函数并使用了withopen语法。当我执行的时候,得到的只是“输入你想要查找的单词”,当我输入这个单词时,执行自动结束我一直在绞尽脑汁不知道哪里出错了,但就是无法发现我的错误。如果您能发现下......
  • 查找字符串中第 n 次出现的子字符串
    这看起来应该是相当微不足道的,但我是Python新手,想要以最Pythonic的方式来做。我想找到与字符串中第n次出现的子字符串相对应的索引。|||必须有一些与我想做的事情相当的东西,即如何在Python中实现这一点?mystring.find("substring",2nd)Howcanyoua......
  • 如何在 PYTHON 中查找输入数字的千位、百位、十位和个位中的数字?例如:256 有 6 个一、5
    num=int(input("Pleasegivemeanumber:"))print(num)thou=int((num//1000))print(thou)hun=int((num//100))print(hun)ten=int((num//10))print(ten)one=int((num//1))print(one)我尝试过这个,但它不起作用,我被困住了。代码几乎是正确的,但需......
  • 折半查找BinarySearch
    折半查找的前提是在有序序列里查找。#include<iostream>usingnamespacestd;intBinarySearch(inta[],intsize,intx){ intleft=0,right=size-1; while(left<=right) { intmid=(left+right)/2; if(a[mid]==x) returnmid; elseif......
  • 二分查找
    二分查找是很经典的算法了,写的时候虽然写对了,后面看了文章才知道细节还是蛮多的。像target所在的区间,有[left,right]和[left,right)两种写法,会极大的影响边界处理条件。实质就在于我们需要在定义的区间内对target进行搜索,而不能有任何遗漏。后面的处理要和前面的区间范围配合。逻......
  • 整体二分学习笔记
    整体二分一般适用于解决可以若干次二分解决的问题,当进行若干次二分的复杂度无法接受时,就可以使用整体二分。可以使用整体二分解决的题目需要满足以下性质:1.询问的答案具有可二分性2.修改对判定答案的贡献互相独立,修改之间互不影响效果3.修改如果对判定答案有贡献,则贡献为一......
  • 造海船(二分基础)
    描述明朝郑和下西洋,需要建造庞大的海船,需要足够的木料,因为那时候没有钢铁制造的船,现在有n根原木,现在想把这些木头切割成k段长度均为l的小段木头(木头有可能有剩余),用来制造船的部件。当然,工匠希望得到的小段木头越长越好,这样可以让船更大一些不浪费木料,请求出l的最大值......
  • 从零开始学Java(超详细韩顺平老师笔记梳理)05——数组(语法,赋值机制,拷贝反转)、排序(冒泡排
    文章目录前言一、数组1.基础语法1)介绍2)使用(动态、静态初始化语法与使用)3)注意事项和细节2.数组赋值机制(ArryAssign)3.数组拷贝4.数组反转(reserve)5.数组的扩容与缩减二、排序三、查找四、二维数组(TwoDimensionalArry)1.快速入门2.使用3.案例:打印一个10行的......