Skip to content

Conversation

@github-actions
Copy link

LeetCode 每日一题:1984. Minimum Difference Between Highest and Lowest of K Scores

题目链接: https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores/
难度: Easy

解题过程

思考过程

我将帮助您解决 LeetCode 第 1984 题。首先,我需要获取题目信息。

思考过程

现在我已经获得了题目信息。题目是"Minimum Difference Between Highest and Lowest of K Scores",难度为简单。函数签名是 int minimumDifference(vector<int>& nums, int k)。这是一个普通题目,所以应该使用 SolutionBase 模式。

首先,我需要理解问题:给定一个整数数组 nums 和一个整数 k,我们需要从数组中选择任意 k 个学生的分数,使得这 k 个分数中最高分和最低分的差值最小。返回可能的最小差值。

分析:

  • 数组长度 n 最大为 1000,k 的取值范围是 1 <= k <= nums.length <= 1000。
  • 我们需要找到最小的差值。观察示例:nums = [9,4,1,7], k=2,答案是 2。选择的 k 个学生是 [9,7],差值为 2。

关键点:由于我们只需要差值(最大值减最小值),所以选择 k 个分数时,如果这 k 个分数在排序后的数组中连续,那么差值会更小吗?实际上,为了最小化最大值和最小值的差,我们应该选择排序后相邻的 k 个元素。因为如果选择的 k 个元素不是连续的,那么最大值和最小值之间的差距会包含更多元素,导致差值更大。这是一个直觉:排序后使用滑动窗口。

算法思路:

  1. 首先对数组进行排序(升序)。
  2. 然后遍历排序后的数组,对于每个起始位置 i(0 <= i <= n-k),计算 nums[i+k-1] - nums[i] 的差值。
  3. 取所有差值中的最小值。

为什么这样有效?因为排序后,数组中任意 k 个元素的最小差值一定出现在某个连续的 k 个元素子数组中。证明:假设我们选择了 k 个元素,其中最小值为 a,最大值为 b。在排序数组中,a 和 b 之间的所有元素都包含在内(如果存在不在选择中的元素,那么我们可以用该元素替换 a 或 b,可能得到更小的差值)。因此,选择连续的 k 个元素可以得到最小的差值。

时间复杂度:排序 O(n log n),滑动窗口 O(n),总复杂度 O(n log n)。空间复杂度:取决于排序算法,通常为 O(log n) 或 O(n)(如果使用额外空间)。由于 n <= 1000,这个算法足够高效。

这题比较简单,不需要多种解法。但我们可以提供两种思路:排序后滑动窗口(最优解),以及暴力枚举所有组合(C(n,k) 太大,不可行)。实际上暴力枚举在 n=1000, k=500 时组合数太大,不可行。所以只实现排序滑动窗口即可。

但是按照示例,需要实现多策略吗?根据指导原则:只有当问题有多种不同的算法思路时,才考虑实现多个策略。这里排序滑动窗口是最优解,暴力枚举不可行。所以只实现一个策略即可。

现在我需要查看数据结构实现吗?这个题目不涉及特定的数据结构,只是数组操作。不需要调用 get_data_structure_implementation。

现在需要生成三个文件:头文件、源文件、测试文件。使用提供的类名和命名空间:

  • solution_class_name: MinimumDifferenceBetweenHighestAndLowestOfKScoresSolution
  • solution_class_base: MinimumDifferenceBetweenHighestAndLowestOfKScores
  • test_class_name: MinimumDifferenceBetweenHighestAndLowestOfKScoresTest
  • namespace: problem_1984

我需要按照普通题目示例(TwoSum)的格式来写。首先设计头文件。

头文件应该包含 SolutionBase 基类,派生类定义 minimumDifference 方法。还要包含构造函数来注册策略。

源文件实现策略函数和构造函数。

测试文件包含测试用例。

现在开始生成代码。

首先,头文件:

思考过程

文件已生成。现在需要编译验证。调用 build_project。

思考过程

编译成功。现在运行测试,验证算法是否正确。

思考过程

所有测试通过。很好。

现在总结一下解题思路和过程。

解题报告:LeetCode 1984 - Minimum Difference Between Highest and Lowest of K Scores

题目理解

题目要求从给定的分数数组中选择任意 k 个学生的分数,使得这 k 个分数中最高分和最低分的差值最小,并返回这个最小差值。

关键点

  • 数组长度 n 最大为 1000,k 的取值范围为 1 ≤ k ≤ n ≤ 1000
  • 分数值范围为 0 到 10^5
  • 需要找到最小的可能差值

算法思路

这个问题可以通过以下观察得到简化的解法:

  1. 排序的重要性:要最小化 k 个元素的最大最小值之差,最直观的想法是让这 k 个元素尽可能接近。在排序后的数组中,连续的元素自然具有较小的差值。

  2. 连续子数组的最优性:可以证明,最小差值一定出现在排序后数组的某个长度为 k 的连续子数组中。

    • 证明思路:假设选择了 k 个元素,最小值为 a,最大值为 b。在排序数组中,a 和 b 之间的所有元素都必然被选中(否则可以用中间元素替换 a 或 b 来得到更小的差值)。因此选择连续的 k 个元素是最优的。
  3. 滑动窗口算法

    • 首先对数组进行升序排序
    • 然后遍历数组,对于每个起始位置 i(0 ≤ i ≤ n-k),计算 nums[i+k-1] - nums[i] 的差值
    • 取所有差值中的最小值作为答案

复杂度分析

  • 时间复杂度:O(n log n),主要来自排序操作。排序后只需 O(n) 的线性扫描。
  • 空间复杂度:O(log n) 或 O(n),取决于排序算法的实现。C++ 的 std::sort 通常使用 O(log n) 的栈空间。

代码实现

采用标准的 SolutionBase 模式实现,支持多策略(虽然本题只实现了一个最优策略)。

关键代码

static int solution1(vector<int>& nums, int k) {
  sort(nums.begin(), nums.end());
  int n = nums.size();
  int minDiff = INT_MAX;
  for (int i = 0; i <= n - k; ++i) {
    int diff = nums[i + k - 1] - nums[i];
    if (diff < minDiff) {
      minDiff = diff;
    }
  }
  return minDiff;
}

测试用例设计

除了题目自带的两个示例外,还设计了以下测试用例以确保代码正确性:

  1. 边界情况:k 等于数组长度 n
  2. 全相同元素:所有分数相同,差值为 0
  3. 递增序列:验证连续子数组的选择
  4. 大数字:验证整数范围的处理

总结

本题是一道简单的贪心/排序问题,核心在于认识到排序后连续子数组的最优性。通过先排序再滑动窗口的方法,可以在 O(n log n) 时间内高效解决问题。对于 n ≤ 1000 的数据规模,这个算法完全足够。

✅ 解题完成!代码已通过编译和测试。


本报告由 AI 自动生成,包含完整的思考过程和解题步骤。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant