Skip to content

Commit

Permalink
fix: 增加一些额外说明
Browse files Browse the repository at this point in the history
  • Loading branch information
lucifer committed Nov 30, 2020
1 parent 8285fc2 commit 9de0985
Showing 1 changed file with 24 additions and 5 deletions.
29 changes: 24 additions & 5 deletions problems/239.sliding-window-maximum.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ https://leetcode-cn.com/problems/sliding-window-maximum/
## 思路

符合直觉的想法是直接遍历 nums, 然后然后用一个变量 slideWindow 去承载 k 个元素,
然后对 slideWindow 求最大值,这是可以的,时间复杂度是 O(n \* k).代码如下:
然后对 slideWindow 求最大值,这是可以的,遍历一次的时间复杂度是 $N$,k 个元素求最大值时间复杂度是 $k$, 因此总的时间复杂度是 O(n \* k).代码如下:

JavaScript:

Expand Down Expand Up @@ -90,18 +90,28 @@ class Solution:
```

但是如果真的是这样,这道题也不会是 hard 吧?这道题有一个 follow up,要求你用线性的时间去完成。
我们可以用双端队列来完成,思路是用一个双端队列来保存`接下来的滑动窗口可能成为最大值的数`。具体做法:

- 入队列
其实,我们没必须存储窗口内的所有元素。 如果新进入的元素比前面的大,那么前面的元素就不再有利用价值,可以直接移除。这提示我们使用一个[单调递增栈](../thinkings/monotone-stack.md "单调栈专题")来完成。

但由于窗口每次向右移动的时候,位于窗口最左侧的元素是需要被擦除的,而栈只能在一端进行操作。

而如果你使用数组实现,就是可以在另一端操作了,但是时间复杂度仍然是 $O(k)$,和上面的暴力算法时间复杂度一样。

因此,我们考虑使用链表来实现,维护两个指针分别指向头部和尾部即可,这样做的时间复杂度是 $O(1)$,这就是双端队列。

因此思路就是用一个双端队列来保存`接下来的滑动窗口可能成为最大值的数`

具体做法:

- 入队列
- 移除失效元素,失效元素有两种

1. 一种是已经超出窗口范围了,比如我遍历到第 4 个元素,k = 3,那么 i = 0 的元素就不应该出现在双端队列中了
具体就是`索引大于 i - k + 1的元素都应该被清除`

2. 小于当前元素都没有利用价值了,具体就是`从后往前遍历(双端队列是一个递减队列)双端队列,如果小于当前元素就出队列`

如果你仔细观察的话,发现双端队列其实是一个递减的一个队列。因此队首的元素一定是最大的。用图来表示就是:
经过上面的分析,不难知道双端队列其实是一个递减的一个队列,因此队首的元素一定是最大的。用图来表示就是:

![](https://tva1.sinaimg.cn/large/007S8ZIlly1ghltxg29buj30hb0di757.jpg)

Expand All @@ -115,6 +125,8 @@ class Solution:

JavaScript:

JS 的 deque 实现我这里没有写, 大家可以参考 [collections/deque](https://github.com/montagejs/collections/blob/master/deque.js)

```js
var maxSlidingWindow = function (nums, k) {
// 双端队列优化时间复杂度, 时间复杂度O(n)
Expand All @@ -140,15 +152,22 @@ var maxSlidingWindow = function (nums, k) {
};
```

**复杂度分析**

- 时间复杂度:$$O(N * k)$$,如果使用双端队列优化的话,可以到 $$O(N)$$
- 空间复杂度:$$O(k)$$

Python3:

```python
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
deque, res, n = collections.deque(), [], len(nums)
for i in range(n):
# 移除前面实现的元素,整因为如此,才需要双端队列
while deque and deque[0] < i - k + 1:
deque.popleft()
# 下面三行,类似单调递增栈
while deque and nums[i] > nums[deque[-1]]:
deque.pop()
deque.append(i)
Expand All @@ -160,7 +179,7 @@ class Solution:
**复杂度分析**

- 时间复杂度:$$O(N)$$
- 空间复杂度:$$O(N)$$
- 空间复杂度:$$O(k)$$

## 扩展

Expand Down

0 comments on commit 9de0985

Please sign in to comment.