leetcode
leetcode 2301 ~ 2350
最小化数对的最大差值

最小化数对的最大差值

难度:

标签:

题目描述

You are given a 0-indexed integer array nums and an integer p. Find p pairs of indices of nums such that the maximum difference amongst all the pairs is minimized. Also, ensure no index appears more than once amongst the p pairs.

Note that for a pair of elements at the index i and j, the difference of this pair is |nums[i] - nums[j]|, where |x| represents the absolute value of x.

Return the minimum maximum difference among all p pairs. We define the maximum of an empty set to be zero.

 

Example 1:

Input: nums = [10,1,2,7,1,3], p = 2
Output: 1
Explanation: The first pair is formed from the indices 1 and 4, and the second pair is formed from the indices 2 and 5. 
The maximum difference is max(|nums[1] - nums[4]|, |nums[2] - nums[5]|) = max(0, 1) = 1. Therefore, we return 1.

Example 2:

Input: nums = [4,2,1,2], p = 1
Output: 0
Explanation: Let the indices 1 and 3 form a pair. The difference of that pair is |2 - 2| = 0, which is the minimum we can attain.

 

Constraints:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109
  • 0 <= p <= (nums.length)/2

代码结果

运行时间: 205 ms, 内存: 30.4 MB


import java.util.Arrays;

/**
 * The idea is the same as the standard Java solution, but here we use streams for sorting and handling operations.
 */
public class Solution {
    public int minimizeMaxDifference(int[] nums, int p) {
        nums = Arrays.stream(nums).sorted().toArray();
        int left = 0, right = nums[nums.length - 1] - nums[0];
        while (left < right) {
            int mid = (left + right) / 2;
            if (canFormPairs(nums, p, mid)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
    private boolean canFormPairs(int[] nums, int p, int maxDiff) {
        int count = 0;
        for (int i = 1; i < nums.length && count < p; i++) {
            if (nums[i] - nums[i - 1] <= maxDiff) {
                count++;
                i++; // skip the next element
            }
        }
        return count >= p;
    }
}

解释

方法:

首先将数组排序,然后计算相邻元素的差值存入diff数组。如果p为0,则直接返回0。定义一个检查函数check,用于检查是否能找到p个差值小于等于upper的数对。使用二分查找在diff的最小值和最大值之间寻找满足条件的最小upper值,并返回该值加上diff的最小值作为结果。

时间复杂度:

O(nlogn)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在实现中首先对数组进行排序?排序对结果的正确性和算法的效率有什么影响?
在实现中首先对数组进行排序是为了使得元素之间的差值计算能够反映出实际上可能组成数对的元素。排序确保了当我们计算相邻元素之间的差值时,这些差值是最小的可能差值,有助于我们后续的处理和判断。排序的正确性在于它允许我们只考虑相邻元素之间的差值,简化了问题的复杂度。对于算法的效率,排序是O(n log n),这可能是整个算法中最耗时的部分,但它是实现算法目标的必要步骤。
🦆
在构建diff数组时,为什么只计算了相邻元素之间的差值?这样做是否能保证找到全局最优的下标对?
在构建diff数组时,只计算相邻元素之间的差值是基于已经进行了排序的前提。排序后的数组中,任何两个非相邻元素之间的差值都会大于或等于它们之间任何相邻元素对的差值。因此,关注相邻元素的差值就足够了,这样可以简化问题而不影响结果的正确性。这种方法确实可以帮助找到全局最优的下标对,因为在排序数组中,最小的差值总是来自相邻元素。
🦆
check函数中为什么要每次跳过一个元素(`i += 2`)来计数满足条件的对?这样做是否能保证不重复使用任何下标?
check函数中的做法(每次跳过一个元素,即`i += 2`)是为了确保每次计数的数对之间没有重叠。这是因为每个数对涉及到两个元素(即当前元素和它的下一个元素),通过跳过一个元素的方式,我们可以保证每次选取的数对是独立的,不会有共享的元素。这样做确实可以保证每个元素最多只被使用一次,从而避免了重复使用任何下标。
🦆
二分查找的目的是寻找满足条件的最小upper值,但为什么最终返回的结果是`ans = bisect_left(...) + min(diff)`,加上min(diff)的原因是什么?
二分查找的目的是寻找能够使得至少p对数的差值小于等于upper的最小可能upper值。但是,这个upper值是基于差值数组diff的相对值(即差值与最小差值之间的差)。因此,为了转换成原数组nums中数对的实际差值,需要将找到的这个最小upper值加上差值数组中的最小值(min(diff))。这样做是因为我们的upper是基于最小差值上的增量,所以加上最小差值才能恢复到原始数对差值的上下文中。

相关问题