diff --git a/README.md b/README.md index d4b870570905ab02f25f9482d8f6afde211f03ab..f7322ae804b55e6a31b55efb47969d66e3f8a2de 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/assets/drawio/239.sliding-window-maximum.drawio b/assets/drawio/239.sliding-window-maximum.drawio new file mode 100644 index 0000000000000000000000000000000000000000..50821c45e743f5de4f5d2fd18deafa40e51734ee --- /dev/null +++ b/assets/drawio/239.sliding-window-maximum.drawio @@ -0,0 +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= \ No newline at end of file diff --git a/assets/problems/239.sliding-window-maximum.png b/assets/problems/239.sliding-window-maximum.png new file mode 100644 index 0000000000000000000000000000000000000000..82d937d64fe5e53c020cc23c5c203eb333f9e34f Binary files /dev/null and b/assets/problems/239.sliding-window-maximum.png differ diff --git a/problems/239.sliding-window-maximum.md b/problems/239.sliding-window-maximum.md new file mode 100644 index 0000000000000000000000000000000000000000..c2464fd016667881aaaee84a883a7f97ca41478d --- /dev/null +++ b/problems/239.sliding-window-maximum.md @@ -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; +}; +``` + +## 扩展 + +### 为什么用双端队列 +因为删除无效元素的时候,会清除队首的元素(索引太小了 +)或者队尾(元素太小了)的元素。 因此需要同时对队首和队尾进行操作,使用双端队列是一种合乎情理的做法。 diff --git a/todo/slideWindow/239.sliding-window-maximum.js b/todo/slideWindow/239.sliding-window-maximum.js deleted file mode 100644 index c39e8b955dea8fec4d47f252ac10802de133c88b..0000000000000000000000000000000000000000 --- a/todo/slideWindow/239.sliding-window-maximum.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * @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) { - // bad 时间复杂度O(n^2) - 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; - // 双端队列优化时间复杂度 -};