🚀 用前缀和高效解决“子数组差绝对值最小值”问题
📘 一、题目描述
给定一个整数数组 nums
,和一个查询数组 queries
,每个查询是 [li, ri]
,表示一个子数组 nums[li...ri]
。请你对每个查询,找出满足 nums[i] != nums[j]
且 li ≤ i < j ≤ ri
的最小的 |nums[i] - nums[j]|
。
若子数组中所有元素都相等(即不存在满足条件的两数),返回 -1
。
✅ 例子说明:
输入:
nums = [5, 2, 3, 7, 2]
queries = [[0, 4], [1, 4], [2, 2]]
输出:
[1, 1, -1]
解释:
- 查询
[0, 4]
:子数组为[5, 2, 3, 7, 2]
,不同元素中最小差值为|2-3|=1
- 查询
[1, 4]
:子数组为[2, 3, 7, 2]
,最小差值仍为 1 - 查询
[2, 2]
:子数组只有一个元素,无法构成两个不相等元素,返回 -1
🧠 二、解题思路分析
本题的难点在于如何快速回答多个区间查询,尤其是在 nums
较长、queries
较多的情况下。
观察题目条件:
- 所有
nums[i]
的值满足1 <= nums[i] <= 100
,数值范围很小。 - 这意味着我们可以使用前缀和 + 数值计数,快速判断一个数是否出现在某个区间。
💡 三、前缀和优化方案
核心思想:
我们构造一个二维数组 pre[i][x]
表示从 nums[0]
到 nums[i-1]
中数字 x
出现的次数。
对于每个查询 [l, r]
:
- 我们判断某个数字
x
是否在nums[l...r]
中出现,只需判断:
pre[r+1][x] - pre[l][x] > 0
- 我们只需从
1
到100
遍历出现的数字,记录相邻数字的最小差值。
✅ 四、Python 实现代码
class Solution:
def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]:
C = 100 # 最大数字值
n = len(nums)
# 构建前缀频次数组 pre[i][x]: 前 i 个数中数字 x 出现的次数
pre = [[0] * (C + 1)]
for num in nums:
pre.append(pre[-1][:])
pre[-1][num] += 1
ans = []
for left, right in queries:
last = 0
best = float("inf")
for j in range(1, C + 1):
if pre[right + 1][j] != pre[left][j]:
if last != 0:
best = min(best, j - last)
last = j
ans.append(-1 if best == float("inf") else best)
return ans
⏱️ 五、时间与空间复杂度分析
- 预处理时间:
O(n * C)
,C = 100
- 每次查询时间:
O(C)
- 总时间复杂度:
O(n * C + q * C)
,其中q
是查询个数 - 空间复杂度:
O(n * C)
用于前缀统计数组
这是一个时间与空间都极其可控的解决方案,非常适合面对数万级别的数据。
🧪 六、更多测试样例
nums = [1, 3, 4, 8, 6]
queries = [[0, 1], [1, 3], [0, 4], [2, 2]]
# 输出: [2, 1, 1, -1]
解释:
[0, 1]
: [1, 3] → 差值 = 2[1, 3]
: [3, 4, 8] → 最小差值 = 1[0, 4]
: [1, 3, 4, 8, 6] → 最小差值 = 1(3 和 4)[2, 2]
: 单个元素 → -1
📌 七、总结与拓展
✅ 总结:
- 充分利用了题目中“数值范围固定”的特性。
- 使用前缀和+频次统计,实现了高效多次区间查询。
- 在大数据量场景中,优于排序法、暴力法数十倍。
📖 可拓展题型:
- 区间中不同元素个数
- 区间中某数字是否存在
- 最接近目标的两个元素差值
- 基于数值范围的小优化(桶思想)
✍️ 八、结语
这道题是“数值范围优化 + 区间统计”题目的经典例子。熟练掌握这种技巧,可以在很多类似题中灵活应对。
如果你喜欢这样的题解风格或想学习更多相关优化技巧,欢迎点赞、收藏、留言交流!