2
2
3
3
在计算机网络中,滑动窗口协议(Sliding Window Protocol)是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。我们所要讲解的滑动窗口算法也是利用了同样的特性。
4
4
5
- > 滑动窗口 (Sliding Window):在给定数组 / 字符串上维护一个固定长度或不定长度的窗口。可以对窗口进行滑动操作、缩放操作,以及维护最优解操作。
5
+ > ** 滑动窗口算法 (Sliding Window)** :在给定数组 / 字符串上维护一个固定长度或不定长度的窗口。可以对窗口进行滑动操作、缩放操作,以及维护最优解操作。
6
6
7
- - 滑动操作:窗口可按照一定方向进行移动。最常见的是向右侧移动。
8
- - 缩放操作:对于不定长度的窗口,可以从左侧缩小窗口长度,也可以从右侧增大窗口长度。
7
+ - ** 滑动操作** :窗口可按照一定方向进行移动。最常见的是向右侧移动。
8
+ - ** 缩放操作** :对于不定长度的窗口,可以从左侧缩小窗口长度,也可以从右侧增大窗口长度。
9
9
10
- 滑动窗口利用了双指针中的快慢指针技巧,我们可以将滑动窗口看做是快慢指针两个指针中间的区间,也可以可以将滑动窗口看做是快慢指针的一种特殊形式 。
10
+ 滑动窗口利用了双指针中的快慢指针技巧,我们可以将滑动窗口看做是快慢指针两个指针中间的区间,也可以将滑动窗口看做是快慢指针的一种特殊形式 。
11
11
12
12
## 2. 滑动窗口适用范围
13
13
14
14
滑动窗口算法一般用来解决一些查找满足一定条件的连续区间的性质(长度等)的问题。该算法可以将一部分问题中的嵌套循环转变为一个单循环,因此它可以减少时间复杂度。
15
15
16
- 根据问题,我们可以将滑动窗口分为以下两种 :
16
+ 按照窗口长度的固定情况,我们可以将滑动窗口题目分为以下两种 :
17
17
18
18
- ** 固定长度窗口** :窗口大小是固定的。
19
19
- ** 不定长度窗口** :窗口大小是不固定的。
@@ -66,26 +66,46 @@ while right < len(nums):
66
66
67
67
#### 3.3.2 题目大意
68
68
69
- 给你一个整数数组 ` arr ` 和两个整数 ` k ` 和 ` threshold ` 。
69
+ ** 描述 ** :给定一个整数数组 ` arr ` 和两个整数 ` k ` 和 ` threshold ` 。
70
70
71
- 要求:返回长度为 ` k ` 且平均值大于等于 ` threshold ` 的子数组数目。
71
+ ** 要求** :返回长度为 ` k ` 且平均值大于等于 ` threshold ` 的子数组数目。
72
+
73
+ ** 说明** :
74
+
75
+ - 1ドル \le arr.length \le 10^5$。
76
+ - 1ドル \le arr[ i] \le 10^4$。
77
+ - 1ドル \le k \le arr.length$。
78
+ - 0ドル \le threshold \le 10^4$。
79
+
80
+ ** 示例** :
81
+
82
+ ``` Python
83
+ 输入:arr = [2 ,2 ,2 ,2 ,5 ,5 ,5 ,8 ], k = 3 , threshold = 4
84
+ 输出:3
85
+ 解释:子数组 [2 ,5 ,5 ],[5 ,5 ,5 ] 和 [5 ,5 ,8 ] 的平均值分别为 4 ,5 和 6 。其他长度为 3 的子数组的平均值都小于 4 (threshold 的值)。
86
+
87
+
88
+ 输入:arr = [11 ,13 ,17 ,23 ,29 ,31 ,7 ,5 ,2 ,3 ], k = 3 , threshold = 5
89
+ 输出:6
90
+ 解释:前 6 个长度为 3 的子数组平均值都大于 5 。注意平均值不是整数。
91
+ ```
72
92
73
93
#### 3.3.3 解题思路
74
94
95
+ ##### 思路 1:滑动窗口(固定长度)
96
+
75
97
这道题目是典型的固定窗口大小的滑动窗口题目。窗口大小为 ` k ` 。具体做法如下:
76
98
77
99
1 . ` ans ` 用来维护答案数目。` window_sum ` 用来维护窗口中元素的和。
78
100
2 . ` left ` 、` right ` 都指向序列的第一个元素,即:` left = 0 ` ,` right = 0 ` 。
79
- 3 . 当窗口元素个数不足 ` k ` 个时,不断移动 ` right ` ,先将 ` k ` 个元素填入窗口中。
80
- 4 . 当窗口元素个数为 ` k ` 时,即:` right - left + 1 >= k ` 时,判断窗口内的元素和平均值是否大于等于阈值。
101
+ 3 . 向右移动 ` right ` ,先将 ` k ` 个元素填入窗口中。
102
+ 4 . 当窗口元素个数为 ` k ` 时,即:` right - left + 1 >= k ` 时,判断窗口内的元素和平均值是否大于等于阈值` threshold ` 。
81
103
1 . 如果满足,则答案数目 + 1。
82
104
2 . 然后向右移动 ` left ` ,从而缩小窗口长度,即 ` left += 1 ` ,使得窗口大小始终保持为 ` k ` 。
83
- 5 . 向右移动 ` right ` ,将元素填入窗口中 。
84
- 6 . 重复 3 ~ 5 步,直到 ` right ` 到达数组末尾 。
105
+ 5 . 重复 3 ~ 4 步,直到 ` right ` 到达数组末尾 。
106
+ 6 . 最后输出答案数目 。
85
107
86
- 最后输出答案数目。
87
-
88
- #### 3.3.4 代码
108
+ ##### 思路 1:代码
89
109
90
110
``` Python
91
111
class Solution :
@@ -109,6 +129,11 @@ class Solution:
109
129
return ans
110
130
```
111
131
132
+ ##### 思路 1:复杂度分析
133
+
134
+ - ** 时间复杂度** :$O(n)$。
135
+ - ** 空间复杂度** :$O(n)$。
136
+
112
137
## 4. 不定长度窗口
113
138
114
139
### 4.1 不定长度窗口求解步骤
@@ -146,23 +171,42 @@ while right < len(nums):
146
171
147
172
#### 4.3.2 题目大意
148
173
149
- 给定一个字符串 ` s ` 。
174
+ ** 描述** :给定一个字符串 ` s ` 。
175
+
176
+ ** 要求** :找出其中不含有重复字符的最长子串的长度。
177
+
178
+ ** 说明** :
150
179
151
- 要求:找出其中不含有重复字符的 最长子串 的长度。
180
+ - 0ドル \le s.length \le 5 * 10^4$。
181
+ - ` s ` 由英文字母、数字、符号和空格组成。
182
+
183
+ ** 示例** :
184
+
185
+ ``` Python
186
+ 输入: s = " abcabcbb"
187
+ 输出: 3
188
+ 解释: 因为无重复字符的最长子串是 " abc" ,所以其长度为 3 。
189
+
190
+
191
+ 输入: s = " bbbbb"
192
+ 输出: 1
193
+ 解释: 因为无重复字符的最长子串是 " b" ,所以其长度为 1 。
194
+ ```
152
195
153
196
#### 4.3.3 解题思路
154
197
155
- 用滑动窗口 ` window ` 来记录不重复的字符个数, ` window ` 为哈希表类型。
198
+ ##### 思路 1:滑动窗口(不定长度)
156
199
157
- 设定两个指针: ` left ` 、 ` right ` ,分别指向滑动窗口的左右边界,保证窗口中没有重复字符 。
200
+ 用滑动窗口 ` window ` 来记录不重复的字符个数, ` window ` 为哈希表类型 。
158
201
159
- - 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
160
- - 将最右侧字符 ` s[right] ` 加入当前窗口 ` window ` 中,记录该字符个数。
161
- - 如果该窗口中该字符的个数多于 1 个,即 ` window[s[right]] > 1 ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新窗口中对应字符的个数,直到 ` window[s[right]] <= 1 ` 。
162
- - 维护更新无重复字符的最长子串长度。然后右移 ` right ` ,直到 ` right >= len(nums) ` 结束。
163
- - 输出无重复字符的最长子串长度。
202
+ 1 . 设定两个指针:` left ` 、` right ` ,分别指向滑动窗口的左右边界,保证窗口中没有重复字符。
203
+ 2 . 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
204
+ 3 . 向右移动 ` right ` ,将最右侧字符 ` s[right] ` 加入当前窗口 ` window ` 中,记录该字符个数。
205
+ 4 . 如果该窗口中该字符的个数多于 1 个,即 ` window[s[right]] > 1 ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新窗口中对应字符的个数,直到 ` window[s[right]] <= 1 ` 。
206
+ 5 . 维护更新无重复字符的最长子串长度。然后继续右移 ` right ` ,直到 ` right >= len(nums) ` 结束。
207
+ 6 . 输出无重复字符的最长子串长度。
164
208
165
- ####4.3.4 代码
209
+ ##### 思路 1: 代码
166
210
167
211
``` Python
168
212
class Solution :
@@ -188,6 +232,11 @@ class Solution:
188
232
return ans
189
233
```
190
234
235
+ ##### 思路 1:复杂度分析
236
+
237
+ - ** 时间复杂度** :$O(n)$。
238
+ - ** 空间复杂度** :$O(| \sum |)$。其中 $\sum$ 表示字符集,$| \sum |$ 表示字符集的大小。
239
+
191
240
### 4.4 长度最小的子数组
192
241
193
242
#### 4.4.1 题目链接
@@ -196,23 +245,43 @@ class Solution:
196
245
197
246
#### 4.4.2 题目大意
198
247
199
- 给定一个只包含正整数的数组 ` nums ` 和一个正整数 ` target ` 。
248
+ ** 描述** :给定一个只包含正整数的数组 ` nums ` 和一个正整数 ` target ` 。
249
+
250
+ ** 要求** :找出数组中满足和大于等于 ` target ` 的长度最小的「连续子数组」,并返回其长度。如果不存在符合条件的子数组,返回 ` 0 ` 。
200
251
201
- 要求:找出数组中满足和大于等于 ` target ` 的长度最小的「连续子数组」,并返回其长度。如果不存在符合条件的子数组,返回 ` 0 ` 。
252
+ ** 说明** :
253
+
254
+ - 1ドル \le target \le 10^9$。
255
+ - 1ドル \le nums.length \le 10^5$。
256
+ - 1ドル \le nums[ i] \le 10^5$。
257
+
258
+ ** 示例** :
259
+
260
+ ``` Python
261
+ 输入:target = 7 , nums = [2 ,3 ,1 ,2 ,4 ,3 ]
262
+ 输出:2
263
+ 解释:子数组 [4 ,3 ] 是该条件下的长度最小的子数组。
264
+
265
+
266
+ 输入:target = 4 , nums = [1 ,4 ,4 ]
267
+ 输出:1
268
+ ```
202
269
203
270
#### 4.4.3 解题思路
204
271
272
+ ##### 思路 1:滑动窗口(不定长度)
273
+
205
274
最直接的做法是暴力枚举,时间复杂度为 $O(n^2)$。但是我们可以利用滑动窗口的方法,在时间复杂度为 $O(n)$ 的范围内解决问题。
206
275
207
276
用滑动窗口来记录连续子数组的和,设定两个指针:` left ` 、` right ` ,分别指向滑动窗口的左右边界,保证窗口中的和刚好大于等于 ` target ` 。
208
277
209
- - 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
210
- - 向右移动 ` right ` ,将最右侧元素加入当前窗口和 ` window_sum ` 中。
211
- - 如果 ` window_sum >= target ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新窗口和的最小值,直到 ` window_sum < target ` 。
212
- - 然后继续右移 ` right ` ,直到 ` right >= len(nums) ` 结束。
213
- - 输出窗口和的最小值作为答案。
278
+ 1 . 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
279
+ 2 . 向右移动 ` right ` ,将最右侧元素加入当前窗口和 ` window_sum ` 中。
280
+ 3 . 如果 ` window_sum >= target ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新窗口和的最小值,直到 ` window_sum < target ` 。
281
+ 4 . 然后继续右移 ` right ` ,直到 ` right >= len(nums) ` 结束。
282
+ 5 . 输出窗口和的最小值作为答案。
214
283
215
- ####4.4.4 代码
284
+ ##### 思路 1: 代码
216
285
217
286
``` Python
218
287
class Solution :
@@ -236,6 +305,11 @@ class Solution:
236
305
return ans if ans != size + 1 else 0
237
306
```
238
307
308
+ ##### 思路 1:复杂度分析
309
+
310
+ - ** 时间复杂度** :$O(n)$。
311
+ - ** 空间复杂度** :$O(1)$。
312
+
239
313
### 4.5 乘积小于K的子数组
240
314
241
315
#### 4.5.1 题目链接
@@ -244,25 +318,40 @@ class Solution:
244
318
245
319
#### 4.5.2 题目大意
246
320
247
- 给定一个正整数数组 ` nums ` 和整数 ` k ` 。
321
+ ** 描述 ** : 给定一个正整数数组 ` nums ` 和整数 ` k ` 。
248
322
249
- 要求 :找出该数组内乘积小于 ` k ` 的连续的子数组的个数。
323
+ ** 要求 ** :找出该数组内乘积小于 ` k ` 的连续的子数组的个数。
250
324
251
- #### 4.5.3 解题思路
325
+ ** 说明 ** :
252
326
253
- 滑动窗口求解。
327
+ - 1ドル \le nums.length \le 3 * 10^4$。
328
+ - 1ドル \le nums[ i] \le 1000$。
329
+ - 0ドル \le k \le 10^6$。
254
330
255
- 设定两个指针: ` left ` 、 ` right ` ,分别指向滑动窗口的左右边界,保证窗口内所有数的乘积 ` window_product ` 都小于 ` k ` 。使用 ` window_product ` 记录窗口中的乘积值,使用 ` count ` 记录符合要求的子数组个数。
331
+ ** 示例 ** :
256
332
257
- - 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
333
+ ``` Python
334
+ 输入:nums = [10 ,5 ,2 ,6 ], k = 100
335
+ 输出:8
336
+ 解释:8 个乘积小于 100 的子数组分别为:[10 ]、[5 ]、[2 ],、[6 ]、[10 ,5 ]、[5 ,2 ]、[2 ,6 ]、[5 ,2 ,6 ]。需要注意的是 [10 ,5 ,2 ] 并不是乘积小于 100 的子数组。
258
337
259
- - 将最右侧元素加入当前子数组乘积 ` window_product ` 中。
260
338
261
- - 如果 ` window_product >= k ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新当前乘积值 ` window_product ` 直到 ` window_product < k ` 。
262
- - 记录累积答案个数 += 1,向右移动 ` right ` ,直到 ` right >= len(nums) ` 结束。
263
- - 输出累积答案个数。
339
+ 输入:nums = [ 1 , 2 , 3 ], k = 0
340
+ 输出: 0
341
+ ```
264
342
265
- #### 4.5.4 代码
343
+ #### 4.5.3 解题思路
344
+
345
+ ##### 思路 1:滑动窗口(不定长度)
346
+
347
+ 1 . 设定两个指针:` left ` 、` right ` ,分别指向滑动窗口的左右边界,保证窗口内所有数的乘积 ` window_product ` 都小于 ` k ` 。使用 ` window_product ` 记录窗口中的乘积值,使用 ` count ` 记录符合要求的子数组个数。
348
+ 2 . 一开始,` left ` 、` right ` 都指向 ` 0 ` 。
349
+ 3 . 向右移动 ` right ` ,将最右侧元素加入当前子数组乘积 ` window_product ` 中。
350
+ 4 . 如果 ` window_product >= k ` ,则不断右移 ` left ` ,缩小滑动窗口长度,并更新当前乘积值 ` window_product ` 直到 ` window_product < k ` 。
351
+ 5 . 记录累积答案个数加 ` 1 ` ,继续右移 ` right ` ,直到 ` right >= len(nums) ` 结束。
352
+ 6 . 输出累积答案个数。
353
+
354
+ ##### 思路 1:代码
266
355
267
356
``` Python
268
357
class Solution :
@@ -290,6 +379,11 @@ class Solution:
290
379
return count
291
380
```
292
381
382
+ ##### 思路 1:复杂度分析
383
+
384
+ - ** 时间复杂度** :$O(n)$。
385
+ - ** 空间复杂度** :$O(1)$。
386
+
293
387
## 参考资料
294
388
295
389
- 【答案】[ TCP 协议的滑动窗口具体是怎样控制流量的? - 知乎] ( https://www.zhihu.com/question/32255109/answer/68558623 )
0 commit comments