题目描述

✅ 910. 最小差值 II

image-20260928225328459

image-20260928225328460

题意分析

每个元素都必须选择加 k 或减 k,目标是让修改后的最大值与最小值之差尽可能小。每项只能取这两个结果,不能在中间任意调整;调整后出现负数是允许的。代码先排序,会改变输入顺序。

解法:排序 + 枚举分割点

核心思路

[!blue]

排序后,只需考虑“前缀全部加 k,后缀全部减 k”的方案。理由是:若较小的数 x 减 k,较大的数 y 加 k,原来的两个结果为 x - k、y + k;交换选择后得到 x + k、y - k,二者仍落在原区间 [x - k, y + k] 内,所以整体极差不会扩大。

反复消除这种“前面减、后面加”的选择,就能把任意方案整理成前加后减,且答案不会变差。因此至少存在一个这种形式的最优解,枚举分界就足够,无需枚举每个元素的两种选择。

分界放在 i 与 i + 1 之间时,前缀最小、最大值分别为 nums[0] + k、nums[i] + k,后缀分别为 nums[i + 1] - k、nums[n - 1] - k。两段内部顺序不变,取两段最大值中的较大者作为 high,最小值中的较小者作为 low,当前极差便是 high - low。

全部加或全部减也属于合法方案,二者极差都等于原极差,用它初始化答案。这样只需枚举两段都非空的内部切分,不会漏掉边界方案。

解题步骤

  • 将数组升序排序,用 nums[n - 1] - nums[0] 初始化答案。
  • 枚举 i = 0..n-2,令左侧加 k、右侧减 k。
  • 计算 high = max(nums[i] + k, nums[n - 1] - k) 和 low = min(nums[0] + k, nums[i + 1] - k),用 high - low 更新答案。
  • 返回所有方案中的最小极差。单元素没有内部分界,直接返回 0;k = 0 时所有方案都保留原极差。

代码实现

class Solution {
    public int smallestRangeII(int[] nums, int k) {
        Arrays.sort(nums);
        int n = nums.length;
        // 全部同向修改的极差等于原极差。
        int answer = nums[n - 1] - nums[0];

        for (int i = 0; i < n - 1; i++) {
            // 分界左侧加、右侧减,分别比较两个分段的极值。
            int high = Math.max(nums[i] + k, nums[n - 1] - k);
            int low = Math.min(nums[0] + k, nums[i + 1] - k);

            answer = Math.min(answer, high - low);
        }

        return answer;
    }
}
import "sort"

func smallestRangeII(nums []int, k int) int {
    sort.Ints(nums)
    n := len(nums)
    // 全部同向修改的极差等于原极差。
    answer := nums[n-1] - nums[0]

    for i := 0; i < n-1; i++ {
        // 分界左侧加、右侧减,分别比较两个分段的极值。
        high := nums[n-1] - k
        if nums[i]+k > high {
            high = nums[i] + k
        }

        low := nums[0] + k
        if nums[i+1]-k < low {
            low = nums[i+1] - k
        }

        if high-low < answer {
            answer = high - low
        }
    }

    return answer
}

复杂度分析

  • 时间复杂度:$O(n\log n)$。排序占主导,枚举分界只需 $O(n)$。
  • 空间复杂度:扫描使用 $O(1)$ 额外空间,另计标准库排序的辅助空间。

关键点总结

[!green]

  • 交换论证证明存在一个分界型最优解,不要求每个最优方案都采用相同形式。
  • 分界后两段各自有序,但两段之间可能交错,所以新极值必须从两段端点共同选取。
  • 原极差是全部同向调整的合法结果,不代表允许所有元素保持不动。

易错点总结

[!yellow]

  • 只修改原最大值和最小值,会忽略中间元素调整后成为新的极值。
  • 只比较分界两边的相邻值,会漏掉两段另外一端的候选极值。
  • 不能把负的调整结果舍弃;本题没有要求修改后的元素非负。
  • 不保留原极差,会漏掉全部加、全部减以及单元素的情况。

相似题目

题目 难度 关联与区别
908. 最小差值 I 简单 原题每项可在±k范围内任意调整,本题只能选择加k或减k,需要保留离散选择约束。
632. 最小区间 困难 每项有x-k与x+k两个候选,可视为从每组取一点的最小覆盖区间;本题统一偏移量允许更简单的排序分割。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2025/73076691
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!