Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
luzhipeng committed Apr 25, 2019
1 parent 6b7b6f9 commit 885350c
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 62 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
- 🆕 [128.longest-consecutive-sequence](./problems/128.longest-consecutive-sequence.md)
- [145.binary-tree-postorder-traversal](./problems/145.binary-tree-postorder-traversal.md)
- [146.lru-cache](./problems/146.lru-cache.md)
- 🆕 [239.sliding-window-maximum](./problems/239.sliding-window-maximum.md)
- 🆕 [295.find-median-from-data-stream.md](./problems/295.find-median-from-data-stream.md)
- [301.remove-invalid-parentheses](./problems/301.remove-invalid-parentheses.md)

Expand Down
1 change: 1 addition & 0 deletions assets/drawio/239.sliding-window-maximum.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<mxfile modified="2019-04-25T03:46:49.398Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" etag="9O6h0S1KGXXQD0X-YjUP" version="10.6.5" type="device"><diagram id="eWjeoaqTePDURmZ7jabo" name="第 1 页">7Zpdb5swFIZ/DZetwOYrl0nWbJq2qVI3deqdC05ABUzBWdL9+tnFJIFDpk1JaqviKnD8gf28x+QcYwvP8+3HipTJVxbTzEJ2vLXwBwshByFX/EjLS2PxQ7sxrKo0VpX2hrv0N1XGtto6jWndqcgZy3hado0RKwoa8Y6NVBXbdKstWdZ9aklWFBjuIpJB630a86SxhijY2z/RdJW0T3b8SVOSk7aymkmdkJhtDkz4xsLzijHeXOXbOc0kvJZL025xpHQ3sIoW/F8a3N6y7z9KEno/H54+0+nDnK6nV7KB7OYXydZqxmq0/KVFULF1EVPZi23h2SZJOb0rSSRLN0J0YUt4nok7R1zWvGJPdM4yVr22xouFbyNflCzTLBuyqwHQitPt0ak5O2DC0yjLKa9eRJW2QestysmQr+43e8kCZUoO1MITZSTKS1a7rvcgxYVi+R9cHYDVOY3rOSihLqUJhOQNQPIuxQi6HtbPKDCLEQaMrvQ7EjLMkTwISb8nIcM8yQeQPO2MsGGOFBj4RsKG+VEIGPnaGbmG+dEEMAr0MzLMj9qQzegQyfF0U4KBpP5XUp/SLlnTRgmGkgaESf1YEgXo2tMMCsaTJ7pTL6tbhhGNoqE88DH0XM++kAMaQNaFLqh/pfYjdezqXqkDsbp5K1U/Jhitv4d1qp8rjPBPzIKM4OraurmePSswgquHdHM9eyZhBtfQ1cu1Hc97ew8M7Hu/LVcj05XANEowCo/ps7jskxJz5n/7vFKwgvZ8T5lIlq4KcRsJRlTYZ5JgGpFsqgryNI7lYwb5dxVasoIfOPDUD+1ZqOzqg50I684ild9VyvFhVO8OKIUuphQMV0elhpTa/d9rUwpGzKNSg0qFoWalYAw+KjWkFHYnmpWCUf2o1JBSboA0KwXzhFGpQaXaDXVdSmGYeYxKDSnlYZgjvq1SMJcZlRpUaqI5SscIKGUhP+Nq9pY8HdiC8Z/X8sDdTOboy9cTaAcmfyV/K1q3zcVwmh6aklH6/gcFP4C59NtqD3NpoFKL9gt5pNktq1OeMon4kXHO8q5+fRk4K3eqtgdAnSH92JpnaSGUb8+hSmViUic7mQZr1Akp5TDz7UoeoL0mmxrH168nV7/dw30l257NF0g+vy6bTpbpVj4BiH3MKcCG1K7LM7hHf6vFxzDZ8HzoHu0KPr97wM9yp7wainU+vhuOie95vd0b+2L/C+J2f3D6tezg+Dm++QM=</diagram></mxfile>
Binary file added assets/problems/239.sliding-window-maximum.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 160 additions & 0 deletions problems/239.sliding-window-maximum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
## 题目地址

https://leetcode.com/problems/sliding-window-maximum/description/

## 题目描述

```
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.
Example:
Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
Note:
You may assume k is always valid, 1 ≤ k ≤ input array's size for non-empty array.
Follow up:
Could you solve it in linear time?
```

## 思路

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

```js
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
// bad 时间复杂度O(n * k)
if (nums.length === 0 || k === 0) return [];
let slideWindow = [];
const ret = [];
for (let i = 0; i < nums.length - k + 1; i++) {
for (let j = 0; j < k; j++) {
slideWindow.push(nums[i + j]);
}
ret.push(Math.max(...slideWindow));
slideWindow = [];
}
return ret;
};
```

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


- 入队列

- 移除失效元素,失效元素有两种

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

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


如果你仔细观察的话,发现双端队列其实是一个递减的一个队列。因此队首的元素一定是最大的。用图来表示就是:

![239.sliding-window-maximum](../assets/problems/239.sliding-window-maximum.png)

## 关键点解析

- 递归简化操作
- 如果树很高,建议使用栈来代替递归
- 这道题目对顺序没要求的,因此队列数组操作都是一样的,无任何区别

## 代码

```js
/*
* @lc app=leetcode id=239 lang=javascript
*
* [239] Sliding Window Maximum
*
* https://leetcode.com/problems/sliding-window-maximum/description/
*
* algorithms
* Hard (37.22%)
* Total Accepted: 150.8K
* Total Submissions: 399.5K
* Testcase Example: '[1,3,-1,-3,5,3,6,7]\n3'
*
* Given an array nums, there is a sliding window of size k which is moving
* from the very left of the array to the very right. You can only see the k
* numbers in the window. Each time the sliding window moves right by one
* position. Return the max sliding window.
*
* Example:
*
*
* Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
* Output: [3,3,5,5,6,7]
* Explanation:
*
* Window position Max
* --------------- -----
* [1 3 -1] -3 5 3 6 7 3
* ⁠1 [3 -1 -3] 5 3 6 7 3
* ⁠1 3 [-1 -3 5] 3 6 7 5
* ⁠1 3 -1 [-3 5 3] 6 7 5
* ⁠1 3 -1 -3 [5 3 6] 7 6
* ⁠1 3 -1 -3 5 [3 6 7] 7
*
*
* Note:
* You may assume k is always valid, 1 ≤ k ≤ input array's size for non-empty
* array.
*
* Follow up:
* Could you solve it in linear time?
*/
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
// 双端队列优化时间复杂度, 时间复杂度O(n)
const deque = []; // 存放在接下来的滑动窗口可能成为最大值的数
const ret = [];
for (let i = 0; i < nums.length; i++) {
// 清空失效元素
while (deque[0] < i - k + 1) {
deque.shift();
}

while (nums[deque[deque.length - 1]] < nums[i]) {
deque.pop();
}

deque.push(i);

if (i >= k - 1) {
ret.push(nums[deque[0]]);
}
}
return ret;
};
```

## 扩展

### 为什么用双端队列
因为删除无效元素的时候,会清除队首的元素(索引太小了
)或者队尾(元素太小了)的元素。 因此需要同时对队首和队尾进行操作,使用双端队列是一种合乎情理的做法。
62 changes: 0 additions & 62 deletions todo/slideWindow/239.sliding-window-maximum.js

This file was deleted.

0 comments on commit 885350c

Please sign in to comment.