Skip to content

Commit 420ffb9

Browse files
authored
feat: add problem $2817
2 parents 125ebf9 + 14afe0a commit 420ffb9

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
## 题目地址(2817. 限制条件下元素之间的最小绝对差)
2+
3+
https://leetcode.cn/problems/minimum-absolute-difference-between-elements-with-constraint
4+
## 题目描述
5+
6+
```
7+
给你一个下标从 0 开始的整数数组 nums 和一个整数 x 。
8+
9+
请你找到数组中下标距离至少为 x 的两个元素的 差值绝对值 的 最小值 。
10+
11+
换言之,请你找到两个下标 i 和 j ,满足 abs(i - j) >= x 且 abs(nums[i] - nums[j]) 的值最小。
12+
13+
请你返回一个整数,表示下标距离至少为 x 的两个元素之间的差值绝对值的 最小值 。
14+
15+
16+
17+
示例 1:
18+
19+
输入:nums = [4,3,2,4], x = 2
20+
输出:0
21+
解释:我们选择 nums[0] = 4 和 nums[3] = 4 。
22+
它们下标距离满足至少为 2 ,差值绝对值为最小值 0 。
23+
0 是最优解。
24+
示例 2:
25+
26+
输入:nums = [5,3,2,10,15], x = 1
27+
输出:1
28+
解释:我们选择 nums[1] = 3 和 nums[2] = 2 。
29+
它们下标距离满足至少为 1 ,差值绝对值为最小值 1 。
30+
1 是最优解。
31+
示例 3:
32+
33+
输入:nums = [1,2,3,4], x = 3
34+
输出:3
35+
解释:我们选择 nums[0] = 1 和 nums[3] = 4 。
36+
它们下标距离满足至少为 3 ,差值绝对值为最小值 3 。
37+
3 是最优解。
38+
39+
40+
提示:
41+
42+
1 <= nums.length <= 105
43+
1 <= nums[i] <= 109
44+
0 <= x < nums.length
45+
```
46+
47+
## 前置知识
48+
49+
- 二分查找
50+
51+
## 思路
52+
53+
### 初始思考与暴力解法
54+
55+
在这个题目里,我首先考虑到的是最简单的方式,也就是暴力破解的方式。这种方法的时间复杂度为O(n^2),但是在题目的提示中还给出了数据范围为`1 <= nums[i] <= 10^9`。这意味着在最坏的情况下数组中的元素值可能非常大,从而导致内层循环的迭代次数也将会巨大,最后可能会出现执行超时的问题。
56+
57+
下面是尝试暴力解法的代码:
58+
```python
59+
class Solution:
60+
def minAbsoluteDifference(self, nums: List[int], x: int) -> int:
61+
n = len(nums)
62+
minDiff = float('inf')
63+
64+
for i in range(n):
65+
for j in range(i + x, n):
66+
absDiff = abs(nums[i] - nums[j])
67+
if absDiff < minDiff:
68+
minDiff = absDiff
69+
70+
return minDiff
71+
72+
```
73+
74+
### 寻求更高效的解决方案
75+
76+
在面对大规模数据或数据范围较大的情况下,我们需要寻找更高效的算法来解决这个题目,以避免超时的问题。为了降低复杂度,我们可以通过维护一个有序集合,并使用二分查找的方式进行更快的插入和查找操作,从而减少迭代次数。
77+
78+
在这个问题中,我们使用二分查找的思路进行优化主要有两个目的:
79+
80+
1. 快速插入:由于我们需要维护一个有序数组,每次插入一个新元素时,如果使用普通的插入方式,可能需要遍历整个数组才能找到插入位置,时间复杂度为O(n)。但是,如果使用二分查找,我们可以在对数时间内找到插入位置,时间复杂度为O(log n)。
81+
2. 快速查找:对于每个索引为 `i + x` 的元素,我们需要在有序数组中找出最接近它的元素。如果使用普通的查找方式,可能需要遍历整个数组才能找到该元素,时间复杂度为O(n)。但是,如果使用二分查找,我们可以在对数时间内找到该元素,时间复杂度为O(log n)。
82+
83+
这种优化策略可以将算法的复杂度从O(n^2)降为O(N log N)。
84+
85+
### 优化策略的具体实现
86+
87+
1. 初始化:定义一个变量 `res` 为无穷大,用于存储最小的绝对差。同时定义一个 `SortedList` 对象 `ls` ,用于存储遍历过的元素并保持其有序性。
88+
2. 遍历数组:使用 `for` 循环遍历 `nums` 数组。
89+
3. 每次循环中,先获取当前元素 `nums[i]`,然后将其添加到有序列表 `ls` 中。
90+
4. 获取 `nums[i + x]`,然后使用 `SortedList.bisect_right` 方法在有序列表 `ls` 中找到最后一个不大于 `nums[i+x]` 的元素的位置 `idx`
91+
5. 使用 `nums[i + x]` 和 `ls[idx - 1]`(即 `nums[i + x]` 在 `ls` 中的前一个元素)的差值更新结果 `res``res` 的值为当前 `res` 和新的差值中的较小值。
92+
6. 如果 `idx` 小于 `ls` 的长度(即 `nums[i + x]` 在 `ls` 中的后一个元素存在),则尝试使用 `nums[i + x]` 和 `ls[idx]` 的差值更新结果 `res`
93+
7. 循环结束后,返回结果 `res`,这是数组中所有相隔 `x` 的元素的最小绝对差。
94+
95+
96+
## 代码
97+
98+
- 语言支持:Python3
99+
100+
Python3 Code:
101+
102+
```python
103+
from sortedcontainers import SortedList
104+
105+
class Solution:
106+
def minAbsoluteDifference(self, nums: List[int], x: int) -> int:
107+
n = len(nums)
108+
109+
# 初始化答案为无穷大
110+
res = float('inf')
111+
112+
# 维护前面元素的有序序列
113+
ls = SortedList()
114+
115+
for i in range(n - x):
116+
117+
# 将nums[i]加入有序序列ls,SortedList保证插入后仍然有序
118+
v = nums[i]
119+
ls.add(v)
120+
121+
# 使用二分查找寻找前面序列中最后一个<=nums[i+x]的元素
122+
v = nums[i + x]
123+
idx = ls.bisect_right(v)
124+
125+
# 使用和nums[i+x]最接近的元素更新答案,将答案更新为当前答案和新差值中的较小值
126+
res = min(res, abs(v - ls[idx - 1]))
127+
128+
# 如果存在更接近的元素,也尝试更新答案
129+
if idx < len(ls):
130+
res = min(res, abs(ls[idx] - v))
131+
132+
return res
133+
```
134+
135+
136+
**复杂度分析**
137+
138+
令 n 为数组长度
139+
140+
- 时间复杂度:$O(nlogn)$
141+
- 空间复杂度:$O(n)$
142+
143+
我们的主要循环是 `for i in range(n - x)`,这个循环会执行大约 `n` 次。在这个循环中,有两个关键操作会影响时间复杂度: `ls.add(v)``ls.bisect_right(v)`
144+
145+
`ls.add(v)` 是一个向 `SortedList` 添加元素的操作,其时间复杂度为 O(log n)。`ls.bisect_right(v)` 是二分查找,其时间复杂度也为 O(log n)。
146+
147+
因此,整个循环的时间复杂度为 O(n) * O(log n) = O(n log n)。这样,我们成功地将原本暴力破解中 O(n^2) 的复杂度优化为了 O(n log n),大大提高了算法的执行效率。

0 commit comments

Comments
 (0)