|
| 1 | +## 题目地址(1906. 查询差绝对值的最小值) |
| 2 | + |
| 3 | +https://leetcode-cn.com/problems/minimum-absolute-difference-queries/ |
| 4 | + |
| 5 | +## 题目描述 |
| 6 | + |
| 7 | +``` |
| 8 | +一个数组 a 的 差绝对值的最小值 定义为:0 <= i < j < a.length 且 a[i] != a[j] 的 |a[i] - a[j]| 的 最小值。如果 a 中所有元素都 相同 ,那么差绝对值的最小值为 -1 。 |
| 9 | +
|
| 10 | +比方说,数组 [5,2,3,7,2] 差绝对值的最小值是 |2 - 3| = 1 。注意答案不为 0 ,因为 a[i] 和 a[j] 必须不相等。 |
| 11 | +
|
| 12 | +给你一个整数数组 nums 和查询数组 queries ,其中 queries[i] = [li, ri] 。对于每个查询 i ,计算 子数组 nums[li...ri] 中 差绝对值的最小值 ,子数组 nums[li...ri] 包含 nums 数组(下标从 0 开始)中下标在 li 和 ri 之间的所有元素(包含 li 和 ri 在内)。 |
| 13 | +
|
| 14 | +请你返回 ans 数组,其中 ans[i] 是第 i 个查询的答案。 |
| 15 | +
|
| 16 | +子数组 是一个数组中连续的一段元素。 |
| 17 | +
|
| 18 | +|x| 的值定义为: |
| 19 | +
|
| 20 | +如果 x >= 0 ,那么值为 x 。 |
| 21 | +如果 x < 0 ,那么值为 -x 。 |
| 22 | +
|
| 23 | + |
| 24 | +
|
| 25 | +示例 1: |
| 26 | +
|
| 27 | +输入:nums = [1,3,4,8], queries = [[0,1],[1,2],[2,3],[0,3]] |
| 28 | +输出:[2,1,4,1] |
| 29 | +解释:查询结果如下: |
| 30 | +- queries[0] = [0,1]:子数组是 [1,3] ,差绝对值的最小值为 |1-3| = 2 。 |
| 31 | +- queries[1] = [1,2]:子数组是 [3,4] ,差绝对值的最小值为 |3-4| = 1 。 |
| 32 | +- queries[2] = [2,3]:子数组是 [4,8] ,差绝对值的最小值为 |4-8| = 4 。 |
| 33 | +- queries[3] = [0,3]:子数组是 [1,3,4,8] ,差的绝对值的最小值为 |3-4| = 1 。 |
| 34 | +
|
| 35 | +
|
| 36 | +示例 2: |
| 37 | +
|
| 38 | +输入:nums = [4,5,2,2,7,10], queries = [[2,3],[0,2],[0,5],[3,5]] |
| 39 | +输出:[-1,1,1,3] |
| 40 | +解释:查询结果如下: |
| 41 | +- queries[0] = [2,3]:子数组是 [2,2] ,差绝对值的最小值为 -1 ,因为所有元素相等。 |
| 42 | +- queries[1] = [0,2]:子数组是 [4,5,2] ,差绝对值的最小值为 |4-5| = 1 。 |
| 43 | +- queries[2] = [0,5]:子数组是 [4,5,2,2,7,10] ,差绝对值的最小值为 |4-5| = 1 。 |
| 44 | +- queries[3] = [3,5]:子数组是 [2,7,10] ,差绝对值的最小值为 |7-10| = 3 。 |
| 45 | +
|
| 46 | +
|
| 47 | + |
| 48 | +
|
| 49 | +提示: |
| 50 | +
|
| 51 | +2 <= nums.length <= 10^5 |
| 52 | +1 <= nums[i] <= 100 |
| 53 | +1 <= queries.length <= 2 * 10^4 |
| 54 | +0 <= li < ri < nums.length |
| 55 | +``` |
| 56 | + |
| 57 | +## 前置知识 |
| 58 | + |
| 59 | +- 前缀和 |
| 60 | +- 离散化 |
| 61 | + |
| 62 | +## 公司 |
| 63 | + |
| 64 | +- 暂无 |
| 65 | + |
| 66 | +## 思路 |
| 67 | + |
| 68 | +由于需要查询任意区间 [ql,qr] 的差的最小值。因此一种简单的思路是对 nums 进行一次排序,之后遍历 [ql,qr] 的所有相邻差,并取最小的即可。这种做法时间复杂度为排序 $nlogn$ + 查询 $n^2$,代入题目数据范围 $2 <= nums.length <= 10^5$,则必然超时。 |
| 69 | + |
| 70 | +一种优化的思路是对 nums 的数据进行离散化,即将值映射到一个大小为 nums 值域大小的数组(由于 1 <= nums[i] <= 100 ,因此对于这道题来说至于大小就是 100)。这样通过遍历值域数组就可以得到最小绝对值差。由于值域大小最多是 100,相比于 $10^5$ 是巨大优化。 |
| 71 | + |
| 72 | +不过值域数组不含索引信息,因此我想求区间 [ql,qr] 的差的最小值则无法直接做到,我们只能求区间 [0,n-1] 的差的最小值。 |
| 73 | + |
| 74 | +那怎么办呢? |
| 75 | + |
| 76 | +我们建立 n 个值域数组,即对每一个索引 i 都建立一个值域数组。这样区间 [ql,qr]的值就可以通过前缀和求得。比如 : |
| 77 | + |
| 78 | +```py |
| 79 | +for i in range(1, 101): |
| 80 | + v = pres[qr+1][i] - pres[ql][i] |
| 81 | +``` |
| 82 | + |
| 83 | +v 就是 i 在 [ql,qr] 的出现次数。 |
| 84 | + |
| 85 | +也就是说**我们可以建议二维数组 pre[i][j] 表示数组 nums 前 i 项 j 出现的次数**,这样可以通过前缀和求出任意区间任意数出现次数。 |
| 86 | + |
| 87 | +剩下的就容易了,具体参考下方代码。 |
| 88 | + |
| 89 | +这种做法本质上空间换时间,即使用值域的空间大小换取嵌套 n 循环的实际,是一种典型的取舍。也就是说如果题目值域很大,比如 $10^7$ ,那就不适合使用此算法了。 |
| 90 | + |
| 91 | +## 关键点 |
| 92 | + |
| 93 | +- 同时对索引和值建立前缀和,即建立二维前缀和 |
| 94 | + |
| 95 | +## 代码 |
| 96 | + |
| 97 | +- 语言支持:Python3 |
| 98 | + |
| 99 | +Python3 Code: |
| 100 | + |
| 101 | +```python |
| 102 | + |
| 103 | +class Solution: |
| 104 | + def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]: |
| 105 | + ans = [] |
| 106 | + n = len(nums) |
| 107 | + pres = [[0] * 101] |
| 108 | + for i, num in enumerate(nums): |
| 109 | + pres.append(pres[-1].copy()) |
| 110 | + pres[-1][num] += 1 |
| 111 | + |
| 112 | + for ql, qr in queries: |
| 113 | + pre = -100 |
| 114 | + cur = 100 |
| 115 | + for i in range(1, 101): |
| 116 | + if pres[qr+1][i] - pres[ql][i] > 0: |
| 117 | + cur = min(cur, i - pre) |
| 118 | + pre = i |
| 119 | + if cur >= 100: ans.append(-1) |
| 120 | + else: ans.append(cur) |
| 121 | + return ans |
| 122 | + |
| 123 | +``` |
| 124 | + |
| 125 | +**复杂度分析** |
| 126 | + |
| 127 | +令 n 为数组长度,q 为 queries 长度,v 为 nums 取值范围(这里是 100)。 |
| 128 | + |
| 129 | +- 时间复杂度:$O(nvq)$ |
| 130 | +- 空间复杂度:$O(nv)$ |
| 131 | + |
| 132 | +> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 |
| 133 | +
|
| 134 | +力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ |
| 135 | + |
| 136 | +以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 |
| 137 | + |
| 138 | +关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 |
| 139 | + |
| 140 | + |
0 commit comments