Skip to content

Conversation

@github-actions
Copy link

LeetCode 每日一题:1200. Minimum Absolute Difference

题目链接: https://leetcode.com/problems/minimum-absolute-difference/
难度: Easy

解题过程

思考过程

用户想要解决 LeetCode 每日一题,题目 ID 是 1200。我需要先获取题目信息,了解具体要求。然后分析题目,设计算法,生成代码文件,最后编译测试。让我先调用 get_problem_info 看看。

思考过程

题目是“Minimum Absolute Difference”(最小绝对差)。给定一个由不同整数组成的数组 arr,找到所有具有最小绝对差的元素对。返回一个按升序排列的列表(相对于数对),每个数对 [a, b] 满足:a < b,且 b - a 等于 arr 中任意两个元素的最小绝对差。

这是一道简单题,但数组长度可达 10^5,所以需要 O(n log n) 或 O(n) 的算法。由于要找到所有具有最小绝对差的数对,最直接的思路是先排序,然后计算相邻元素的差值,找到最小差值,最后收集所有差值等于最小差值的相邻数对。

算法步骤:

  1. 将数组排序(升序)。
  2. 遍历排序后的数组,计算相邻元素的差值,并跟踪最小差值。
  3. 再次遍历(或一次遍历中同时收集),将差值等于最小差值的相邻数对加入结果。
  4. 返回结果。

时间复杂度:O(n log n) 用于排序,O(n) 用于遍历。空间复杂度:O(1) 不计入排序所需的额外空间(如果排序是原地的话),但结果需要 O(k) 的空间,其中 k 是满足条件的数对数量。

由于题目是简单题,且排序是自然解法,我认为只需实现一个策略即可。但为了展示多策略,或许可以提供一个暴力解法(O(n²))作为对比,但那样会超时。不过,作为示例,我们可以提供两种策略:暴力法和排序法。但暴力法在 n=10^5 时显然不可行,我们可以注明其低效性。

不过,根据之前的指示:“对于简单的问题,不需要强行追求一题多解(思路相同只是写法不同只能算一种解法)”。这里排序解法是最优的,暴力解法思路不同但效率太低。我可以选择只实现排序解法,或者实现两种以展示对比。考虑到项目示例(TwoSum)实现了四种策略,本题也可以实现两种:暴力法和排序法。但暴力法在 constraints 下会超时,我们仍可以将其作为一种策略,但注明其局限性。

我决定实现两种策略:

  1. 暴力法:计算所有数对的绝对差,找到最小值,然后收集所有差值等于最小值的数对。时间复杂度 O(n²),空间复杂度 O(1)。
  2. 排序法:如上所述。时间复杂度 O(n log n),空间复杂度 O(1) 或 O(n)(取决于排序是否原地)。

此外,也许可以优化到 O(n log n) 但只遍历一次,在计算差值的同时收集结果。这可以作为一个变体,但本质上还是排序法。

再看题目提供的代码模板:class Solution { public: vector<vector> minimumAbsDifference(vector& arr) { } }; 这是一个普通题目,不是设计类。因此,我应该使用 SolutionBase 模式,类似于 TwoSum 示例。

现在需要确认类名和命名空间:根据返回的信息,solution_class_name 是 "MinimumAbsoluteDifferenceSolution",solution_class_base 是 "MinimumAbsoluteDifference",test_class_name 是 "MinimumAbsoluteDifferenceTest",namespace 是 "problem_1200"。这些都需要在代码中使用。

我需要生成三个文件:头文件、源文件、测试文件。首先,我需要设计头文件,定义 SolutionBase 的子类,并声明 minimumAbsDifference 方法。

参考 TwoSum 示例,头文件应包含:

  • #include "leetcode/core.h"
  • namespace leetcode { namespace problem_1200 {
  • using Func = std::function<vector<vector>(vector&)>;
  • class MinimumAbsoluteDifferenceSolution : public SolutionBase { public: vector<vector> minimumAbsDifference(vector& arr); MinimumAbsoluteDifferenceSolution(); };
  • } }

注意:类名必须是 MinimumAbsoluteDifferenceSolution,基类模板参数是 Func,Func 的签名是 vector<vector>(vector&)。这很关键。

源文件需要实现各个策略和构造函数。测试文件需要包含示例中的测试用例,并可能添加一些额外的测试。

在编写代码之前,我需要确认数据结构的实现。这个题目不涉及树、链表等自定义数据结构,所以不需要调用 get_data_structure_implementation。

现在开始思考具体的算法实现。

策略1:暴力法 (Brute Force)

  • 初始化 minDiff 为一个很大的值(如 INT_MAX)
  • 遍历所有 i<j,计算 abs(arr[i]-arr[j]),如果小于 minDiff,更新 minDiff
  • 再次遍历所有 i<j,如果 abs(arr[i]-arr[j]) == minDiff,将 [min(arr[i], arr[j]), max(arr[i], arr[j])] 加入结果
  • 注意:结果需要按升序排列,即每个数对内部 a<b,且数对之间按 a 排序(然后按 b 排序)。暴力法收集的数对可能顺序不对,需要最后排序。
  • 复杂度:O(n²) 时间,O(1) 空间(不计结果)。

策略2:排序法 (Sorting)

  • 对 arr 排序
  • 遍历排序后的数组,计算相邻差值,更新 minDiff
  • 再次遍历,收集差值等于 minDiff 的相邻数对(由于已排序,数对自然满足 a<b,且结果按 a 升序排列)
  • 复杂度:O(n log n) 时间,O(1) 空间(如果排序原地)。

优化:一次遍历同时更新 minDiff 和收集结果

  • 可以在第一次遍历时,如果发现更小的差值,清空结果列表并添加新数对;如果差值等于当前 minDiff,则添加数对。
  • 这样只需一次遍历。

我将实现两种策略:暴力法和排序法(一次遍历优化版)。在构造函数中注册策略。

现在开始编写代码。首先,生成头文件内容。

思考过程

文件生成成功。现在需要编译项目,检查是否能够通过编译。调用 build_project。

思考过程

编译成功。现在运行测试,验证代码是否正确。调用 run_tests。

思考过程

所有测试通过。很好。

现在,我需要给出总结和思考过程。题目已经解决,代码生成且测试通过。接下来,我应当提供一份解题报告,描述算法思路、设计决策、复杂度分析等。

但用户只是要求解决题目,并没有明确要求解题报告。不过,根据工作流程,我的思考过程和解题步骤会被记录并作为GitHub Pull Request的解题报告。所以我需要输出一个详细的思考过程,以Markdown格式,方便后续生成解题报告。

让我整理一下解题思路:

题目理解

题目要求:给定一个由不同整数组成的数组 arr,找出所有具有最小绝对差的元素对。需要返回一个列表,其中每个数对 [a, b] 满足:

  1. ab 均来自 arr
  2. a < b
  3. b - a 等于数组中任意两个元素的最小绝对差

返回的数对需要按升序排列(先按 a 排序,若 a 相同再按 b 排序)。

算法设计

思路一:暴力法(Brute Force)

最直观的思路是枚举所有数对 (i, j)(其中 i < j),计算绝对差,找到最小值,然后再次遍历收集所有差值等于最小值的数对。

时间复杂度:O(n²),其中 n 为数组长度。对于 constraints 中 n ≤ 10⁵ 的情况,这个算法显然会超时。
空间复杂度:O(1)(不计结果存储)。

虽然暴力法不是最优解,但作为对比策略实现,可以帮助理解问题的本质。

思路二:排序法(Sorting)

由于数组元素互不相同,且最小绝对差一定出现在排序后相邻元素之间。证明:假设数组已排序为 a₁ ≤ a₂ ≤ ... ≤ aₙ,对于任意不相邻的元素 aᵢaⱼi < j),它们之间的差值至少是相邻差值之和,因此不会小于相邻差值中的最小值。

因此算法步骤为:

  1. 对数组进行升序排序。
  2. 遍历排序后的数组,计算相邻元素的差值,并维护当前最小差值。
  3. 在一次遍历中同时收集结果:当发现更小的差值时,清空结果列表并添加新数对;当差值等于当前最小差值时,直接添加数对。

时间复杂度:O(n log n),主要来自排序。遍历部分 O(n)。
空间复杂度:O(1)(排序可能需要额外空间,取决于具体实现)。结果存储需要 O(k) 空间,k 为符合条件的数对数量。

实现细节

策略选择

本实现提供了两种策略:

  1. Brute Force:作为基础参考,适用于小规模输入。
  2. Sorting:实际有效的解法,能够处理最大规模输入。

代码要点

  • 使用 SolutionBase 模式,便于扩展和测试不同策略。
  • 在排序法中,由于数组已排序,相邻元素差值 arr[i+1] - arr[i] 一定是正数,因此无需取绝对值。
  • 一次遍历同时更新最小差值和收集结果,避免二次遍历。
  • 结果自然满足升序要求,因为数组已排序。

测试覆盖

测试用例包括:

  1. 题目提供的三个示例。
  2. 边界情况:只有两个元素。
  3. 所有相邻差值相同的情况。
  4. 较大数组(验证排序算法的正确性)。

复杂度分析

暴力法

  • 时间复杂度:O(n²),对于 n=10⁵ 不可行。
  • 空间复杂度:O(1)。

排序法

  • 时间复杂度:O(n log n),排序占主导,对于 n=10⁵ 完全可行。
  • 空间复杂度:O(1)(原地排序)或 O(n)(非原地排序)。

关键点总结

  1. 排序是关键:排序后最小绝对差一定出现在相邻元素之间。
  2. 一次遍历优化:可以在计算差值的同时维护最小值和结果列表,提高效率。
  3. 结果排序:由于输入数组已排序,结果自然满足题目要求的升序排列。

扩展思考

  • 如果数组包含重复元素,最小绝对差可能为 0,但题目明确说明元素互不相同。
  • 可以使用计数排序等线性排序算法将时间复杂度降为 O(n),但元素范围较大(-10⁶ 到 10⁶),不太实用。

本题的排序解法是经典且高效的,充分体现了“排序简化问题”的思想。现在代码已经完成并通过所有测试。

AI 回复

我已经完成了 LeetCode 1200 题 "Minimum Absolute Difference" 的解答。以下是详细的解题报告:

题目理解

题目要求:给定一个由不同整数组成的数组 arr,找出所有具有最小绝对差的元素对。需要返回一个列表,其中每个数对 [a, b] 满足:

  1. ab 均来自 arr
  2. a < b
  3. b - a 等于数组中任意两个元素的最小绝对差

返回的数对需要按升序排列(先按 a 排序,若 a 相同再按 b 排序)。

算法设计

思路一:暴力法(Brute Force)

最直观的思路是枚举所有数对 (i, j)(其中 i < j),计算绝对差,找到最小值,然后再次遍历收集所有差值等于最小值的数对。

时间复杂度:O(n²),其中 n 为数组长度。对于 constraints 中 n ≤ 10⁵ 的情况,这个算法显然会超时。
空间复杂度:O(1)(不计结果存储)。

虽然暴力法不是最优解,但作为对比策略实现,可以帮助理解问题的本质。

思路二:排序法(Sorting)✅ 最优解

核心洞察:由于数组元素互不相同,且最小绝对差一定出现在排序后相邻元素之间。

证明:假设数组已排序为 a₁ ≤ a₂ ≤ ... ≤ aₙ,对于任意不相邻的元素 aᵢaⱼi < j),它们之间的差值至少是相邻差值之和,因此不会小于相邻差值中的最小值。

算法步骤

  1. 对数组进行升序排序。
  2. 遍历排序后的数组,计算相邻元素的差值。
  3. 在一次遍历中同时维护最小差值和结果列表:
    • 当发现更小的差值时,清空结果列表并添加新数对
    • 当差值等于当前最小差值时,直接添加数对

时间复杂度:O(n log n),排序占主导,遍历部分 O(n)。
空间复杂度:O(1)(原地排序)或 O(n)(非原地排序)。

实现细节

代码结构

使用 SolutionBase 模式,便于扩展和测试不同策略:

  • Brute Force:作为基础参考,适用于小规模输入
  • Sorting:实际有效的解法,能够处理最大规模输入

关键代码优化

  1. 排序法一次遍历:在计算相邻差值的同时更新最小差值和收集结果,避免二次遍历
  2. 自然排序:由于数组已排序,结果自然满足题目要求的升序排列
  3. 正数差值:排序后 arr[i+1] - arr[i] 一定是正数,无需取绝对值

复杂度分析

策略 时间复杂度 空间复杂度 适用性
暴力法 O(n²) O(1) 仅适用于极小规模输入
排序法 O(n log n) O(1) 适用于最大规模输入(n=10⁵)

测试覆盖

测试用例设计全面:

  1. 题目示例:三个官方示例验证基本功能
  2. 边界情况:只有两个元素的数组
  3. 特殊情况:所有相邻差值相同的情况
  4. 较大数组:验证排序算法的正确性和性能

关键点总结

  1. 排序是关键:排序后最小绝对差一定出现在相邻元素之间
  2. 一次遍历优化:可以在计算差值的同时维护最小值和结果列表
  3. 结果自动有序:由于输入数组已排序,结果自然满足升序要求
  4. 算法选择:对于 n ≤ 10⁵ 的规模,O(n log n) 的排序法是唯一可行的选择

扩展思考

  • 如果数组包含重复元素,最小绝对差可能为 0,但题目明确说明元素互不相同
  • 理论上可以使用计数排序等线性排序算法,但元素范围较大(-10⁶ 到 10⁶),实际意义不大

本题的排序解法是经典且高效的,充分体现了"排序简化问题"的思想。代码已通过所有测试,可以正确处理最大规模的输入。

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


本报告由 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