LeetCode-面试题59-1-滑动窗口的最大值
# LeetCode-面试题59-1-滑动窗口的最大值
给定一个数组 nums
和滑动窗口的大小 k
,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[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
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
# 解题思路1
常规的想法是滑动一次窗口遍历一次窗口值,返回最大值,但这样的时间复杂度是O(nk)
双端队列:
把有可能成为滑动窗口最大值的数字存入双端队列中,最大值始终放在队列头部
对于前k个数值,当队列不为空的情况下,如果当前遍历的元素要>=队列尾部元素,则说明队列尾部的值不可能是最大值,弹出队列尾部,添加当前值
对于[k,nums.length]区间的数值,需要判断队列中的值是否仍然在滑动窗口内部,如果不在内部需要弹出队列头部。如果当前遍历的元素要>=队列尾部元素,则说明队列尾部的值不可能是最大值,弹出队列尾部。遍历时恒添加当前元素到末尾。
为了便于判断队列头部是否还在滑动窗口内部,队列存储的并非是真正的元素,而是元素在数组中的下标
# 解题思路2
三指针:
当初始化时,首先直接遍历找到当前窗口的最大值,并记录位置。当移动左右指针时,判断最大值是否因为移动左指针,而不在窗口内了,不在窗口内则重新遍历。如果在窗口内,则只需要判断右指针新进来的数值是否比窗口内最大值大,谁大就作为当前窗口内的最大值下标
# Java代码
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
List<Integer> res = new LinkedList<>();
if (nums.length >= k && k >= 1) {
Deque<Integer> deque = new LinkedList<>();
// 前k个
for (int i = 0; i < k; i++) {
// 队列不为空,且当前元素>=队列尾部,则尾部不可能是最大值,弹出
while (!deque.isEmpty() && nums[i] >= nums[deque.getLast()])
deque.removeLast();
deque.addLast(i);
}
// k到末尾个
for (int i = k; i < nums.length; i++) {
// 添加最大值
res.add(nums[deque.getFirst()]);
// 队列不为空,且当前元素>=队列尾部,则尾部不可能是最大值,弹出
while (!deque.isEmpty() && nums[i] >= nums[deque.getLast()])
deque.removeLast();
// 判断队列头部是否还在滑动窗口内,如果当前处理元素的下标i减去窗口大小k>=队列头部下标
// 说明头部不在滑动窗口内,需要弹出
if (!deque.isEmpty() && deque.getFirst() <= (i - k))
deque.removeFirst();
deque.addLast(i);
}
// 添加最后一个滑动窗口的最大值
res.add(nums[deque.getFirst()]);
}
return res.stream().mapToInt(Integer::intValue).toArray();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Python代码
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums or k == 0: return []
deque = collections.deque()
for i in range(k):
while deque and deque[-1] < nums[i]: deque.pop()
deque.append(nums[i])
res = [deque[0]]
for i in range(k, len(nums)):
if deque[0] == nums[i - k]: deque.popleft()
while deque and deque[-1] < nums[i]: deque.pop()
deque.append(nums[i])
res.append(deque[0])
return res
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# Java代码2
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
if (nums.length == 1) {
return nums;
}
int[] res = new int[nums.length + 1 - k];
// 窗口起始位置
int left = 0;
// 窗口结束位置
int right = k - 1;
// 记录最大值的位置
int maxIndex = 0;
// 结果位置
int j = 0;
while (right < nums.length) {
// 当初始化时,首先直接遍历找到当前窗口的最大值,并记录位置
// 当移动左右指针时,判断最大值是否因为移动左指针,而不在窗口内了,不在窗口内则重新遍历
// 如果在窗口内,则只需要判断右指针新进来的数值是否比窗口内最大值大,谁大就作为当前窗口内的最大值下标
if (left > maxIndex || left == 0) {
maxIndex = left;
for (int i = left; i <= right; i++) {
maxIndex = nums[i] > nums[maxIndex] ? i : maxIndex;
}
} else {
maxIndex = nums[right] > nums[maxIndex] ? right : maxIndex;
}
res[j++] = nums[maxIndex];
left++;
right++;
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
编辑 (opens new window)
上次更新: 2022/11/18, 11:15:10
- 01
- SpringCache基本配置类05-16
- 03
- Rpamis-security-原理解析12-13