【问题标题】:Minimum absolute difference of two arrays, at most one replacement两个数组的最小绝对差,最多替换一次
【发布时间】:2022-06-30 04:51:31
【问题描述】:

给定两个长度相同的整数数组 a 和 b。

让我们将a和b之间的差异定义为对应元素的绝对差异之和:

  差异 = |a[0] - b[0]| + |a[1] - b[1]| …

您可以将 a 的 一个 元素替换为 a 的任何其他元素。
您的任务是返回 a 和 b 之间的最小可能差异,这可以通过对 a 执行最多一次此类替换来实现。
您也可以选择保持数组不变。

例子

对于 a = [1, 3, 5] 和 b = [5, 3, 1] ,输出应该是 solution(a, b) = 4。

  • 如果我们保持数组 a 不变,则差异:|1 - 5| + |3 - 3| + |5 - 1| = 8。
  • 如果我们将 a[0] 替换为 a[1],我们会得到
    [3, 3, 5] 差是 |3 - 5| + |3 - 3| + |5 - 1| = 6;
  • 如果我们将 a[0] 替换为 a[2],我们会得到
    [5, 3, 5] 差是 |5 - 5| + |3 - 3| + |5 - 1| = 4;
  • 如果我们将 a[1] 替换为 a[0],我们会得到
    [1, 1, 5] 差是 |1 - 5| + |1 - 3| + |5 - 1| = 10;
  • 如果我们将 a[1] 替换为 a[2],我们会得到
    [1, 5, 5] 差是 |1 - 5| + |5 - 3| + |5 - 1| = 10;
  • 如果我们将 a[2] 替换为 a[0],我们会得到
    [1, 3, 1] 差是 |1 - 5| + |5 - 3| + |1 - 1| = 4;
  • 如果我们将 a[2] 替换为 a[1],我们会得到
    [1, 3, 3] 差是 |1 - 5| + |3 - 3| + |3 - 1| = 6;

所以最终答案是 4。

解决方案最多只能是 O(nlogn) 复杂度。

【问题讨论】:

  • 问题是什么?
  • 编写一个最多线性复杂度的解决方案
  • 扫描两个数组的绝对差异,并在最大堆中跟踪它们。将a 的元素排序到一个单独的数组中。一个接一个地从最大堆中取出元素,并在已排序的a 副本中进行二进制搜索,以寻找减少绝对差异的补码。一旦你找到了一对将差异减小到大于或等于最大堆中的下一个项目的对,你就找到了你的解决方案,因为你可以确定没有更好的对。这是线性的,但可能不是最佳的。
  • attribute&credit properlyCode up a solution… 是一个需求 - 你有问题吗?
  • eeeeewwww,作业呕吐。做这项工作,然后用你的话问一个问题。不要只是把你的作业丢在这里,希望有人会为你做。

标签: algorithm time-complexity


【解决方案1】:

总体思路是这样的:扫描两个数组的绝对差异,并在最大堆中跟踪两个数组中的个体 abs 差异和对应元素。这使您可以开始检查最有希望的。对A 的元素进行排序。一个接一个地从最大堆中取出元素,并在已排序的A 副本中进行二进制搜索,以寻找减少绝对差异的补码。一旦你找到了一对将差异减小到大于或等于最大堆中的下一个项目的对,你就找到了你的解决方案,因为你可以确定没有更好的对。这是线性的,但可能不是最佳的。

import heapq, bisect

def best_diff(A, B): 
    hp = [] # max heap store
    res = 0 # sum of diffs

    for a, b in zip(A, B): 
        hp.append((-abs(a-b), a, b)) 
        res += abs(a-b) 
    heapq.heapify(hp) # heapify once to keep it linear
    A_sorted = list(sorted(A)) 
    best_change = float("-inf") # we want to maximize this

    while hp: 
        diff, a, b = heapq.heappop(hp) 
        diff = abs(diff) 

        # we are looking for an a as close to b as possible
        # to minimize diff and maximize change
        idx = bisect.bisect_left(A_sorted, b) 
        new_diff = abs(A_sorted[idx]-b) 
        if new_diff < diff: 
            best_change = max(best_change, diff - new_diff) 

            # if the change we have achieved is greater than
            # the next diff in the max heap, we bail out early
            # because we can't do better
            if best_change >= -hp[0][0]: 
                return res - best_change 
                     
    return res - best_change

# simple test
# In [11]: best_diff([1,3,5], [5,3,1])                                  
# Out[11]: 4

时间复杂度分析:我们对AB 进行线性循环。这是O(n),其中nAB 的长度。 Heapify 也是O(n)。对A 进行排序并(在最坏的情况下)将A 的所有元素从堆中弹出是线性的,O(n log n)。所以这变成了O(n log n)

空间复杂度:O(n) 用于堆。我们可以就地排序并避免额外的数组。

请注意:我还没有对代码进行足够的测试。仅在您的输入示例上运行它。据我了解,这个想法和概念是正确的,但可能存在我没有想到的极端情况。例如:如果所有As 元素都小于绝对差异怎么办?然后bisect 返回并且idx 等于A 的长度,我们在没有检查的情况下使用它,它将通过一个超出范围的错误。另一个极端情况是idx-1 处的元素与我们正在寻找的目标的绝对差异小于idx 处的元素。

我把它留给你来塑造。

【讨论】:

    【解决方案2】:

    目标是为b 中的所有元素找到a 中最相似的数字,因为可以通过替换其中一对来减少最小绝对差。

    使用 bisect 算法为一个元素找到一个相似的数字需要O(log n)。所以总时间复杂度将是O(n log n)

    这是解决方案。

    import bisect
    
    
    class Solution(object):
        def minAbsoluteSumDiff(self, nums1, nums2):
            """
            :type nums1: List[int]
            :type nums2: List[int]
            :rtype: int
            """
            MOD = 10**9+7
    
            sorted_nums1 = sorted(nums1)
            result = max_change = 0
            for i in xrange(len(nums2)):
                diff = abs(nums1[i]-nums2[i])
                result = (result+diff)%MOD
                if diff < max_change:
                    continue
                j = bisect.bisect_left(sorted_nums1, nums2[i])
                if j != len(sorted_nums1):
                    max_change = max(max_change, diff-abs(sorted_nums1[j]-nums2[i]))
                if j != 0:
                    max_change = max(max_change, diff-abs(sorted_nums1[j-1]-nums2[i]))
            return (result-max_change)%MOD
    

    【讨论】:

      【解决方案3】:

      总体思路是“搜索插入位置”。 如果我们想减少a[i] - b[i] 的差异,我们需要从数组 A 中找到一个最接近我们的b[i] 的数字。因此,我们可以将 A 深度复制到 A' 并对其进行排序,然后进行二进制搜索以找到将我的 b[i] 插入 A' 的位置。结束位置的编号只能等于或小于b[i]

      例如,

      a[0] = 5. b[0] = 10 
      A = {5, 11, 7, 8, 12} A'={5,7,8,11,12} B = {10, x, x, x, x}
      

      我的二分搜索将在索引 3 处停止,即 8,所以我最接近的数字可以是 8 或 11(它的下一个位置)

      这是我的java解决方案(希望你能理解):

      public class ReduceDifferenceByReplacing {
          public static void main(String[] args) {
      //        int[] a = {9064, -5167, -2855, 2333, 7177, 1584, 7621, 9954, 6687, 5, -6459, -8069, 8265, 4601};
      //        int[] b = {8366, -5167, -2855, 2333, 7177, 1584, 7621, -2741, 6687, 5, 4062, 514, 8265, 4601};
              int[] a = {1, 3, 5};
              int[] b = {5, 3, 1};
              System.out.println(solution(a, b));
          }
      
          public static int solution(int[] a, int[] b) {
              int res = 0;
              int[] sorted = a.clone();
              Arrays.sort(sorted);
      
              int n = a.length;
              int maxDiff = 0;
              for (int i = 0; i < n; i++) {
                  res += Math.abs(a[i] - b[i]);
                  if (a[i] != b[i]) {
                      maxDiff = Math.max(maxDiff, binarySearch(i, b[i], sorted));
                  }
              }
              return res - maxDiff;
          }
      
          public static int binarySearch(int start, int target, int[] a) {
              int index = searchInsertPosition(target, a);
              if (index == 0 || index == a.length - 1) {
                  return hasShortedDistance(start, index, start, a) ? Math.abs(a[start] - target) - Math.abs(a[index] - target) : 0;
              } else {
                  int candidate1 = hasShortedDistance(start, index, target, a) ? Math.abs(a[start] - target) - Math.abs(a[index] - target) : 0;
                  int candidate2 = hasShortedDistance(start, index + 1, target, a) ? Math.abs(a[start] - target) - Math.abs(a[index] - target) : 0;
                  return Math.max(candidate1, candidate2);
              }
          }
      
          private static boolean hasShortedDistance(int start, int index, int target, int[] a) {
              return Math.abs(a[index] - target) < Math.abs(a[start] - target);
          }
      
          public static int searchInsertPosition(int target, int[] a) {
              int start = 0;
              int end = a.length - 1;
              while (start <= end) {
                  int mid = (start + end) / 2;
                  if (a[mid] == target) {
                      return mid;
                  } else if (a[mid] < target) {
                      start = mid + 1;
                  } else {
                      end = mid - 1;
                  }
              }
              return start;
          }
      }
      

      【讨论】:

        【解决方案4】:
        int solution(int[] a, int[] b) {
        int sum=0;
        TreeSet<Integer> set = new TreeSet<>();
        for(int i=0;i<a.length;i++){
            sum+=Math.abs(a[i]-b[i]);
            set.add(a[i]);
        }
        int temp=sum;
        for(int i=0;i<b.length;i++){
            int tp = temp;
            int low=Integer.MIN_VALUE, high=Integer.MAX_VALUE;
            if(set.floor(b[i])!=null) low = set.floor(b[i]);
            if(set.ceiling(b[i])!=null) high = set.ceiling(b[i]);
            
            tp-=Math.abs(a[i]-b[i]);
            
            if(low !=Integer.MIN_VALUE) sum=Math.min(sum,tp+Math.abs(low-b[i]));
            if(high !=Integer.MAX_VALUE) sum=Math.min(sum,tp+Math.abs(high-b[i]));
        }
        return sum;
        

        }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多