Open azl397985856 opened 2 years ago
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.offer(new int[]{nums[i], i});
while (pq.peek()[1] <= i - k) {
pq.poll();
}
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
}
Time Complexity: O(nlogn) Space Complexity: O(n)
var maxSlidingWindow = function (nums, k) { // 队列数组(存放的是元素下标,为了取值方便) const q = []; // 结果数组 const ans = []; for (let i = 0; i < nums.length; i++) { // 若队列不为空,且当前元素大于等于队尾所存下标的元素,则弹出队尾 while (q.length && nums[i] >= nums[q[q.length - 1]]) { q.pop(); } // 入队当前元素下标 q.push(i); // 判断当前最大值(即队首元素)是否在窗口中,若不在便将其出队 while (q[0] <= i - k) { q.shift(); } // 当达到窗口大小时便开始向结果中添加数据 if (i >= k - 1) ans.push(nums[q[0]]); } return ans; };
var maxSlidingWindow = function(nums, k) {
if(!nums.length){
return []
}
if(nums.length<k){
return Math.max(...nums)
}
let res = []
for(let i= 0;i<nums.length-k+1;i++){
let max = nums[i];
for(let j=i;j<k+i;j++){
max=Math.max(max,nums[j])
}
res.push(max)
}
return res
};
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if len(nums) < 2:
return nums
queue = deque()
res = [0] * (len(nums)-k+1)
for i in range(len(nums)):
while queue and nums[queue[-1]] <= nums[i]:
queue.pop()
queue.append(i)
if queue[0] <= i - k:
queue.popleft()
if i + 1 >= k:
res[i+1-k] = nums[queue[0]]
return res
https://leetcode-cn.com/problems/sliding-window-maximum/
使用双向队列,队列中保存数的index,需要从大到小排序。如果当前数比队尾的大,则弹出尾部直到满足单调性。当移动到满足k时,当前最大值即为队首的值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums == null || nums.length == 0) {
return new int[0];
}
Deque<Integer> deque = new LinkedList<>();
int[] res = new int[nums.length + 1 - k];
for(int i = 0; i < nums.length; i++) {
if(!deque.isEmpty() && deque.peekFirst() == i - k) {
deque.poll();
}
while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.removeLast();
}
deque.offerLast(i);
if((i + 1) >= k) {
res[i + 1 - k] = nums[deque.peek()];
}
}
return res;
}
}
O(n)
O(n)
优先队列
``
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[0] != o1[0] ? o2[0] - o1[0] : o2[1] - o1[1];
}
});
int len = nums.length;
for (int i = 0; i < k; i++) {
queue.offer(new int[]{nums[i], i});
}
int[] ans = new int[len - k +1];
ans[0] = queue.peek()[0];
for (int i = k; i < len; i++) {
queue.offer(new int[]{nums[i], i});
//如果最大值是k区间之外的就弹出
while (queue.peek()[1] <= i - k) {
queue.poll();
}
ans[i - k + 1] = queue.peek()[0];
}
return ans;
}
}
时间复杂度:O(nlogn)
空间复杂度:O(n)
https://leetcode-cn.com/problems/sliding-window-maximum/
bruteForce, minStack
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
# 思路
# 利用双端队列记录当前滑动窗口的元素索引
# 队列最左侧元素记录滑动窗口中最大元素的索引
# 遍历数组:
# 如果队列最左侧索引已不在滑动窗口范围内,弹出队列最左侧索引
# 通过循环确保队列的最左侧索引所对应元素值最大
# 新元素入队
# 从第一个滑动窗口的末尾索引开始将最大值存储到结果res中
res = []
deque = collections.deque()
for idx, num in enumerate(nums):
# 当最大元素脱离窗口时 表明此时它已经不是最大元素了
if deque and deque[0] == (idx - k):
deque.popleft()
# 更新此时的最大元素 维护一个单调递减栈
while deque and nums[deque[-1]] < num:
deque.pop()
deque.append(idx)
# 此时表明窗口已经初始化完成 需要开始计算最大值了
if idx >= k - 1:
res.append(nums[deque[0]])
return res
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
import heapq
heap = []
res = []
for i in range(k):
heapq.heappush(heap,[-nums[i],i])
res.append(-heap[0][0])
for i in range(k,len(nums)):
heapq.heappush(heap,[-nums[i],i])
while heap[0][1] <= i - k:
print(heap[0][1],heap)
heapq.heappop(heap)
res.append(-heap[0][0])
return res
思路:
使用单调队列。
窗口在划动时,其实只有最左边的元素出去,和最右边的元素进来。所以我们可以考虑使用deque。
deque维护了一个单调队列,因为位于最大值左边的元素是没有用处的,所以我们的队首维护的是窗口最大值。同样的,位于最大值的右侧的小值也是无用的,但是当最大值被划出窗口时,存在小值进行替补的情况,所以队列右侧应该是一个单调递减的情况。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length;
Deque<Integer> deque = new LinkedList<>();
for(int i = 0; i < k ; i++){
while(!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]){
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[len - k + 1];
ans[0] = nums[deque.peekFirst()];
for(int i = k; i < len ; i ++){
while(!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]){
deque.pollLast();
}
deque.offerLast(i);
while(deque.peekFirst() <= i - k){
deque.pollFirst();
}
ans[i- k + 1] = nums[deque.peekFirst()];
}
return ans;
}
}
时间复杂度:O(n)
空间复杂度:O(k)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length;
int resLen = len - k + 1;
int[] res = new int[resLen];
// 0位置存储数组中的值,1位置存储数组的下标
// 大顶堆(元素大小不同按元素大小排列,元素大小相同按下标进行排列)
PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((p1, p2) -> {
if (p1[0] == p2[0]) {
return p2[1] - p1[1];
}
return p2[0] - p1[0];
});
// 初始化优先队列
for (int i = 0; i < k; i++) {
priorityQueue.offer(new int[]{nums[i], i});
}
res[0] = priorityQueue.peek()[0];
for (int i = k; i < len; i++) {
priorityQueue.offer(new int[]{nums[i], i});
while (priorityQueue.peek()[1] <= i - k) {
priorityQueue.poll();
}
res[i - k + 1] = priorityQueue.peek()[0];
}
return res;
}
}
维持一个单调队列, 这样第一个数 一定是 此时窗口的最大值
每次有一个新的数, 那么如果这个数比队尾的数大, 就一直 pop 知道队列为空或者队尾的数比这个数大为止
因为之前的数如果比这个数小, 那么之前的数就没有可能成为任何之后 window 的最大值了, 现在这个值比之前的数 cover 更多 后面的 window, 并且比之前的数大, 所以可以直接 pop, 不影响计算结果
如果队列的第一个数已经不在现在这个 window 内, 那么也 可以 pop 第一个数, 因为不在现在计算的 window 范围之内
然后 当 i ≥ k-1 时, 最大的数 一定在 window 的第一个, 所以将这个数添加到 res 数组
最后返回 res 数组
from collections import deque
import math
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
window = deque()
l = len(nums)
res = []
for i in range(l):
j = i-k+1
while len(window) > 0 and nums[i] >= window[-1][0]:
window.pop()
if len(window) > 0 and window[0][1] < j:
window.popleft()
window.append([nums[i], i])
if i >= k-1:
res.append(window[0][0])
# move to the next position
i += 1
return res[:]
时间复杂度: O(N) 每个数最多被放入 window 一次, 并且最多被 pop 一次, 所以复杂度是 O(N)
空间复杂度: O(k), window 的最大 size 是 k 个元素
class Solution {
private:
class MyQueue { //单调队列(从大到下)
public:
//使用deque来实现单调队列
deque<int> que;
//队列的插入
//维护单调队列(从大到小),就判断要插入的数与队列的末尾大小
//若小于末尾,则插入;若大于末尾,则弹出;
void push(int value) {
while (!que.empty() && value > que.back())
que.pop_back();
que.push_back(value);
}
//队列的删除
//每次弹出都要比较要弹出的数值是否等于队列的出口元素数值,如果相等则弹出
//pop要判断当前队列是否为空
void pop(int value) {
if (!que.empty() && value == que.front())
que.pop_front();
}
//查询当前队列的最大值,即只要返回当前队列的出口就行
int front() {
return que.front();
}
};
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue que;
vector<int> res;
for (int i = 0; i < k; i ++)
que.push(nums[i]);
res.push_back(que.front());
for (int i = k; i < nums.size(); i ++) {
que.pop(nums[i - k]);
que.push(nums[i]);
res.push_back(que.front());
}
return res;
}
};
保持一个单调递减的双端队列
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums:
return []
result = []
q = collections.deque()
for i in range(len(nums)):
while q and i - q[0] >= k:
q.popleft()
while q and nums[i] >= nums[q[-1]]:
q.pop()
q.append(i)
start_index = i + 1 - k
if start_index >= 0:
result.append(nums[q[0]])
return result
思路: 创建一个deque记录当前滑动窗口里数值的index, 重点是维持队列的单调递减性,当新进入队列的元素大于deque的最后一个元素的时候 pop deque[-1], 当deque长度大于k的时候 pop 队首的元素, 答案数组ans记录deque队首的元素 Python 3 Code:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q = deque()
ans = []
for i in range(len(nums)):
while q and nums[i] >= nums[q[-1]]:
q.pop()
while q and i - q[0] >= k:
q.popleft()
q.append(i)
if i >= k - 1:
ans.append(nums[q[0]])
return ans
TC:O(N) SC:O(N)
The most challanging part is to find the next biggest item in the window if the biggeest is going to leave the window. To solve that, we could use a linklist to sort the keys, the head always points to the bigest key in the window. But this would make the overall time complexity to be O(n*k). To improve that, we could modify the linklist, instead of storing every key in the window, we could instead only store what's neccessary.
Think of a situation which we have [8, 4, 3] in the window, now we have push 5 into the window, since 5 is bigger than 4 and 3, we don't really need to keep 4 and 3 in the window anymore, after we remove 8, [4,3,5] the biggest key in the window would be 5, if we remove 4, [3,5] the biggest key would still be 5.
Algo: Sort the linklist descendingly to get the biggest value in the window. The head of the linklist always poinst to the biggest item in the window, when add a new key, compare with the tail of the list and remove any node that is less than the new key.
class Solution {
public:
typedef struct ListNode{
int val;
ListNode* pre;
ListNode* next;
ListNode():pre(NULL), next(NULL){};
}ListNode;
void slideIn(ListNode* head, ListNode* tail, int in) {
ListNode* node = tail->pre;
while (node != head && node->val < in) {
ListNode* pre = node->pre;
pre->next = node->next;
node->next->pre = pre;
delete node;
node = pre;
}
ListNode* newNode = new ListNode();
newNode->val = in;
newNode->next = node->next;
node->next->pre = newNode;
node->next = newNode;
newNode->pre = node;
}
void slideOut(ListNode* head, ListNode* tail, int out) {
if (head->next != tail && head->next->val == out) {
ListNode* node = head->next;
head->next = node->next;
node->next->pre = head;
delete node;
}
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
if (!nums.size())
return result;
ListNode* head = new ListNode();
ListNode* tail = new ListNode();
head->next = tail;
tail->pre = head;
// Buildup the initial window
for (int i = 0; i < k; i++) {
slideIn(head, tail, nums[i]);
}
result.push_back(head->next->val);
for (int i = k; i < nums.size(); i++) {
slideOut(head, tail, nums[i-k]);
slideIn(head, tail, nums[i]);
result.push_back(head->next->val);
}
return result;
}
};
O(n), since each key would only enter and exit the list once, it only takes O(n)
O(k), used for the list
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
q = collections.deque() #q存下标
ans = []
for i in range(len(nums)):
#删除掉小数
while q and nums[q[-1]] <= nums[i]:
q.pop()
#删除掉窗口之外的数
while q and i-q[0] >= k:
q.popleft()
q.append(i)
if i >= k-1:
ans.append(nums[q[0]])
return ans
时间复杂度:O(N) 空间复杂度:O(K)
单调栈。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> num;
for (int i = 0; i < k - 1; i++) {
while (!num.empty() && num.back() < nums[i]) num.pop_back();
num.push_back(nums[i]);
}
for (int i = k - 1; i < nums.size(); i++) {
while (!num.empty() && num.back() < nums[i]) num.pop_back();
num.push_back(nums[i]);
res.push_back(num.front());
if (num.front() == nums[i - k + 1]) num.pop_front();
}
return res;
}
};
严格单调递减的双端队列。当新元素比队尾元素大时,位置在它之前的且值比它小的元素就失去了意义无需储存。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> pq = new LinkedList<>();
for (int i = 0; i < k; i++) {
while (!pq.isEmpty() && nums[i] > nums[pq.peekLast()]){
pq.pollLast();
}
pq.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[pq.peekFirst()];
for (int i = k; i < n; i++){
while (!pq.isEmpty() && nums[i] > nums[pq.peekLast()]){
pq.pollLast();
}
pq.offerLast(i);
if (pq.peekFirst() < i - k + 1){
pq.pollFirst();
}
ans [i - k + 1] = nums[pq.peekFirst()];
}
return ans;
}
}
https://leetcode.com/problems/sliding-window-maximum/
Hard
Medium
单调栈 and deque
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q, result = deque([]), []
for i in range(len(nums)):
while q and q[-1] < nums[i]:
q.pop()
q.append(nums[i])
if i >= k - 1:
if i >= k and nums[i-k] == q[0]:
q.popleft()
result.append(q[0])
return result
时间复杂度: O(N) 空间复杂度:O(K)
单调栈
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
Deque<Integer> dq = new LinkedList<Integer>();
for (int i = 0; i < k; i++) {
while (!dq.isEmpty() && dq.peekLast() < nums[i]) dq.pollLast();
dq.offerLast(nums[i]);
}
res[0] = dq.peekFirst();
for (int i = k; i < nums.length; i++) {
if (dq.peekFirst() == nums[i - k]) dq.pollFirst();
while (!dq.isEmpty() && dq.peekLast() < nums[i]) dq.pollLast();
dq.offerLast(nums[i]);
res[i - k + 1] = dq.peekFirst();
}
return res;
}
}
Explanation
Python
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
deque = collections.deque()
result = []
for i in range(len(nums)):
while deque and nums[deque[-1]] < nums[i]:
deque.pop()
while deque and deque[0] <= i - k:
deque.popleft()
deque.append(i)
if i >= k-1:
result.append(nums[deque[0]])
return result
Complexity:
O(N)
O(N)
Use a deque to store the index of the array. Iterate the array and check if the first element of the deque is out of the sliding window, if so, remove it. Also remove from the right side of the deque whose value is less than the ith value (since smaller values are not going to be recorded). Add index to deque. If i is larger than the length of the sliding window, record the max value.
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
int[] res = new int[nums.length-k+1];
int max = 0;
for (int i = 0; i < nums.length; ++i) {
while (!deque.isEmpty() && deque.peek() <= i-k) {
deque.removeFirst();
}
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.offer(i);
if (i >= k-1) {
res[max++] = nums[deque.peek()];
}
}
return res;
}
}
deque+单调栈,单调栈能够保证最大的window的index在队头。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums or k==0:
return []
if k == 1:
return nums
q = collections.deque()
res = []
for i in range(k):
while q and nums[i]>nums[q[-1]]:
q.pop()
q.append(i)
res.append(nums[q[0]])
for i in range(k,len(nums)):
if q[0]<i-k+1:
q.popleft()
while q and nums[i]>nums[q[-1]]:
q.pop()
q.append(i)
res.append(nums[q[0]])
return res
复杂度分析
用deque来存储index, 若是deque.peek() 的index小于 k的范围, 删除 将k范围内小于nums[i] 的对应index 删除
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length < k) {
return new int[0];
}
List<Integer> tempRes = new ArrayList<>();
Deque<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
//queue的最前点已经超出k的范围, 删除
if (!queue.isEmpty() && queue.peek() < i - k + 1) {
queue.poll();
}
//将不需要的删除, 由deque的结尾开始
while (!queue.isEmpty() && nums[queue.peekLast()] < nums[i]) {
queue.pollLast();
}
queue.offer(i);
if (i >= k - 1) {
tempRes.add(queue.peek());
}
}
int[] res = new int[tempRes.size()];
for (int i = 0; i < tempRes.size(); i++) {
res[i] = nums[tempRes.get(i)];
}
return res;
}
}
deque/prioroty queue. 对于deque,队首元素始终是窗口内的最大元素。对于一个新的元素,这个元素如果比队中在它之前的元素都大,这个元素就是当前的最大值。窗口收缩的条件是窗口内元素个数>k,如果该元素也是队首元素,那么就要出队列。 窗口每次增加1,使用for循环更加方便。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> deque = new LinkedList<>();
int start = 0;
int end = 0;
int n = nums.length;
int[] ret = new int[n - k + 1];
for (int i = 0; i < n; i++) {
while (!deque.isEmpty() && nums[i] > nums[deque.peekLast()]) {
deque.pollLast();
}
if (!deque.isEmpty() && deque.peekFirst() == i - k) {
deque.pollFirst();
}
deque.offerLast(i);
if (i - k + 1 >= 0) {
ret[i - k + 1] = nums[deque.peekFirst()];
}
}
return ret;
}
}
时间:O(n)
空间:O(k)
Hard to think using double side monotonic queue can make it O(N) time complexity to solve it. Queue needs to keep decreasing with head of queue = max
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
#monotonic queue
#a. when adding element, pop all the elements that are smaller than it to keep queu monotonic
#b. when removing element, check two conditions:
# 1. it is not inside sliding window
# 2. it is smaller than current element (referring to a)
deq = collections.deque()
ans = []
for i in range(len(nums)):
while deq and nums[deq[-1]] <= nums[i]:
deq.pop() # keep monotonic
while deq and i - deq[0] >= k:
deq.popleft() # remove outside sliding window range from left
deq.append(i)
if i >= k - 1:
ans.append(nums[deq[0]])
return ans
单调队列
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<Integer>();
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()];
for (int i = k; i < n; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
while (deque.peekFirst() <= i - k) {
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
}
时间复杂度: O(N) 空间复杂度:O(K)
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length
k == 1, nums
k > nums.length, global max
Method 1:
maxHeap
keep adding, once reaches size = k, remove the least recent
Time: O(nlogk), n = nums.length
Space: O(k)
Method 2:
用一个双端队列来保存接下来的滑动窗口可能成为最大值的数。
没必须存储窗口内的所有元素。 如果新进入的元素比前面的大,那么前面的元素就不再有利用价值,可以直接移除。这提示我们使用一个单调递增栈来完成。-> 双端队列其实是一个递减的一个队列 (队首的元素一定是最大的)
入队列 (index instead of val)
移除失效元素,失效元素有两种:1. 出窗口 2. 小于当前元素
// deque: head tail
// big <- small
Time: O(n)
Space: O(k)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (k == 1) return nums;
Deque<Integer> mono = new ArrayDeque<>();
int[] maxes = new int[nums.length - k + 1];
int windowIndex = 0;
for (int i = 0; i < nums.length; i++) {
while (!mono.isEmpty() && nums[mono.peekLast()] <= nums[i]) {
mono.pollLast();
}
// out of window
if (!mono.isEmpty() && i - mono.peekFirst() + 1 > k) {
mono.pollFirst();
}
mono.offer(i);
if (i >= k - 1) {// we are generating windows now
maxes[windowIndex++] = nums[mono.peekFirst()];
}
}
return maxes;
}
public int[] maxSlidingWindow1(int[] nums, int k) {
if (k == 1) return nums;
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) ->(b - a));
int[] maxes = new int[nums.length - k + 1];
int maxIndex = 0;
for (int i = 0; i < nums.length; i++) {
maxHeap.offer(nums[i]);
// window size reaches
if (maxHeap.size() == k) {
maxes[maxIndex++] = maxHeap.peek();
// remove the least recent element
maxHeap.remove(nums[i - k + 1]);
}
}
return maxes;
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
while (!q.isEmpty() && nums[i] > nums[q.peekLast()]) {
q.pollLast();
}
q.offerLast(i);
if (q.peekFirst() <= i - k) q.pollFirst();
if (i >= k-1) res[i-k+1] = nums[q.peekFirst()];
}
return res;
}
}
Time: O{N}\ Space: O(K)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
int left = 0, right = k + left - 1;
while (right < nums.length) {
int[] temp = Arrays.copyOfRange (nums, left, right + 1);
Arrays.sort(temp);
res[left] = temp[k -1];
left++;
right++;
}
return res;
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
int left = 0, right = k + left - 1;
//create a max pq, put the initial window element inside
PriorityQueue<Integer> pq = new PriorityQueue(new Comparator<Integer> (){
public int compare(Integer x, Integer y) {
// System.out.println(x);
return nums[y] - nums[x];
}
});
for (int i = 0; i < right; i++) {
pq.offer(i);
}
while (right < nums.length) {
pq.offer(right);
while (left > pq.peek()) {
pq.poll();
}
res[left] = nums[pq.peek()];
left++;
right++;
}
return res;
}
}
利用单调队列保存元素index,保持队列单调递减且元素均在窗口内。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
l = len(nums)
q = deque()
ans = []
for i in range(l):
# 单调
while q and nums[q[-1]] <= nums[i]:
q.pop()
# 超出窗口
while q and i - q[0] >= k:
q.popleft()
q.append(i)
if i >= k - 1:
ans.append(nums[q[0]])
return ans
复杂度
[Day 28] (239. 滑动窗口最大值)
https://leetcode-cn.com/problems/sliding-window-maximum/
思路:要求线性时间复杂度,使用单调递增栈,窗口每次向右移动,窗口最左侧的元素需要被擦除,因此用双端队列保存滑动窗口可能成为最大的值
def maxSlidingWindow(nums, k):
q = collections.deque() # 本质就是单调队列
ans = []
for i in range(len(nums)):
while q and nums[q[-1]] <= nums[i]: q.pop() # 当前队列最后一位索引对应的数小于当前数就移除队列中的索引出去,直至大于或者q为空
while q and i - q[0] >= k: q.popleft() # 移除失效元素 i-q[0]代表当前窗口,大于k则要把队列左边的移除,直至满足窗口
q.append(i)
if i >= k - 1: ans.append(nums[q[0]]) #当i大于2时,每次保留q中的最左边的值
return ans
复杂度:
学习题解思路:单调队列
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
q = collections.deque() # 本质就是单调队列
ans = []
for i in range(len(nums)):
while q and nums[q[-1]] <= nums[i]: q.pop() # 维持单调性
while q and i - q[0] >= k: q.popleft() # 移除失效元素
q.append(i)
if i >= k - 1: ans.append(nums[q[0]])
return ans
复杂度分析
https://leetcode.com/problems/sliding-window-maximum/
Use heap (PriorityQueue)
remove
method, can keep more than k elements in the heap. When trying to get the max element from the heap, peek
first, check the index of the element, keep poll
until it's within the current window, and add it to the result.poll
costs O(logk).offer
each element to the queuepeek
the top and add to resultoffer
each element to the queuepeek
the top and validate the indexpoll
all the invalid elementspeek
the valid top and add to resultUse deque
pollLast
all the elements smaller than the new elmentofferLast
new element to the dequepeekFirst
the max and add to resultpollLast
all the elements smaller than the new elmentofferLast
new element to the dequepeekFirst
the top and validate the indexpollFirst
all the invalid elementspeekFirst
the valid max and add to resultUse heap (PriorityQueue)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] result = new int[n - k + 1];
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>(){
public int compare(int[] pair1, int[] pair2){
return pair1[0] != pair2[0]? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
// process the first window
for(int i = 0; i < k; i++){
queue.offer(new int[]{nums[i], i});
}
result[0] = queue.peek()[0]; // the max of the first window, it's guaranteed within the window
// slide window to process the rest of the input array, i is the right index of the window
for(int i = k; i < n; i++){
queue.offer(new int[]{nums[i], i});
// the max of the queue, it's not guaranteed within the current window
while(queue.peek()[1] <= i - k){
queue.poll();
}
result[i - k + 1] = queue.peek()[0];
}
return result;
}
}
Use deque
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
int[] result = new int[n - k + 1];
Deque<Integer> deque = new LinkedList<>();
// offer from last, poll from first
// index i is increasing in the deque (last index is the max index)
// nums[i] is decreasing in the deque (first value is the max value)
for(int i = 0; i < k; i++){ // process the first window
while(!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]){
deque.pollLast();
}
deque.offerLast(i);
}
result[0] = nums[deque.peekFirst()]; // for the first window, the first element is guranteed within the first window
for(int i = k; i < n; i++){ // process the rest of the array
while(!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]){
deque.pollLast();
}
deque.offerLast(i);
while(deque.peekFirst() <= i - k){
deque.pollFirst();
}
result[i - k + 1] = nums[deque.peekFirst()];
}
return result;
}
}
offer
and poll
for each element to/from the heap (n is the length of input array).func maxSlidingWindow(nums []int, k int) []int {
q := []int{}
push := func(i int) {
for len(q) > 0 && nums[i] >= nums[q[len(q)-1]] {
q = q[:len(q)-1]
}
q = append(q, i)
}
for i := 0; i < k; i++ {
push(i)
}
n := len(nums)
ans := make([]int, 1, n-k+1)
ans[0] = nums[q[0]]
for i := k; i < n; i++ {
push(i)
for q[0] <= i-k {
q = q[1:]
}
ans = append(ans, nums[q[0]])
}
return ans
}
class Solution:
def maxSlidingWindow(self, nums: 'List[int]', k: 'int') -> 'List[int]':
n = len(nums)
if n * k == 0:
return []
if k == 1:
return nums
left = [0] * n
left[0] = nums[0]
right = [0] * n
right[n - 1] = nums[n - 1]
for i in range(1, n):
# from left to right
if i % k == 0:
# block start
left[i] = nums[i]
else:
left[i] = max(left[i - 1], nums[i])
# from right to left
j = n - i - 1
if (j + 1) % k == 0:
# block end
right[j] = nums[j]
else:
right[j] = max(right[j + 1], nums[j])
output = []
for i in range(n - k + 1):
output.append(max(left[i + k - 1], right[i]))
return output
space time O(n)
思路:队列+滑动窗口
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//新建一个双端队列:Deque<Character> deque = new LinkedList<Character>();
//判断是否为空: deque.isEmpty()
//增:deque.offerFirst(), deque.offerLast();//从头添加和尾部添加
//删:deque.poll/removeFirst(), deque.poll/removeLast()//从头添加和尾部出队
//查:deque.peekFirst(), deque.peekLast()//查看头添加和尾部元素
int len = nums.length;
if (len == 0){
return nums;
}
int[] arr = new int[len - k + 1];
int arrIndex = 0;
//需要维护一个单调递增的双向队列
Deque<Integer> deque = new LinkedList<>();
//先将第一个窗口的值按照规则入队
for(int i = 0; i < k; i++){
while(!deque.isEmpty() && deque.peekLast() < nums[i]){
deque.removeLast();
}
deque.offerLast(nums[i]);
}
//存到数组里,队头元素
arr[arrIndex++] = deque.peekFirst();
//移动窗口
for(int j = k; j < len; j++){
//对应则是窗口的前一个元素最大 变成了队头元素,说明当前窗口没有该值了,则需出队该值,更换最大值
if(nums[j - k] == deque.peekFirst()){
deque.removeFirst();
}
while(!deque.isEmpty() && deque.peekLast() < nums[j]){
deque.removeLast();
}
deque.offerLast(nums[j]);
arr[arrIndex++] = deque.peekFirst();
}
return arr;
}
}
时间复杂度:O(N) 空间复杂度:O(N)
思路:使用单调栈来完成。维护一个单调递增的栈,值得注意的是如果上次移除的数刚好是最大的数,则需要将栈的第一个数手动移除。
class Solution { public int[] maxSlidingWindow(int[] nums, int k) { //使用单调栈 来完成。 //使用单调栈来完成。 int len = nums.length; Stack<Integer> stack = new Stack<>(); for(int i=0;i<k;i++){ if(!stack.isEmpty()){ int temp = nums[i]; while (!stack.isEmpty()&&stack.peek()<temp){ stack.pop(); } stack.add(temp); }else { stack.add(nums[i]); } } int res[] = new int[len-k+1]; int j = 0; res[j++] = stack.get(0); for(int i=k;i<len;i++){ if(!stack.isEmpty()&&nums[i-k] == stack.get(0))stack.remove(0); int temp = nums[i]; while (!stack.isEmpty()&&stack.peek()<temp){ stack.pop(); } stack.add(temp); res[j++] = stack.get(0); } return res; } }
优先级队列模拟大顶堆
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
PriorityQueue<int[]> pd = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] pair1, int[] pair2) {
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
for(int i=0;i<k;i++){
pd.offer(new int[]{nums[i],i});
}
int[] res = new int[nums.length-k+1];
res[0]=pd.peek()[0];
for(int i=0;i<nums.length-k;i++){
pd.offer(new int[]{nums[k+i],k+i});
while(pd.peek()[1]<=i){
pd.poll();
}
res[i+1]=pd.peek()[0];
}
return res;
}
}
时间复杂度:O(nlogn) 空间复杂度:O(n)
使用单调递减队列维护当前窗口的最大值,窗口离开该最大值索引时弹出队列
JavaScript Code
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
var maxSlidingWindow = function(nums, k) {
const res = [], q = [];
let i = 0;
while (i < nums.length) {
while (q.length && nums[i] >= nums[q[q.length - 1]]) {
q.pop();
}
q.push(i);
while (q[0] <= i - k) {
q.shift();
}
if (i >= k - 1) res.push(nums[q[0]]);
i++;
}
return res;
};
时间复杂度:O(n) 数组中每个元素最多只被push和pop一次
空间复杂度:O(k)
var maxSlidingWindow = function (nums, k) {
const res = [];
for (let i = 0; i <= nums.length - k; i++) {
let cur = maxInSlidingWindow(nums, i, i + k);
res.push(cur);
}
return res;
};
function maxInSlidingWindow(nums, start, end) {
let max = -Infinity;
for (let i = start; i < end; i++) {
max = Math.max(nums[i], max);
}
return max;
}
单调队列
使用语言:Python3
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
que = collections.deque()
res = []
for i in range (len(nums)):
while que and nums[que[-1]] <= nums[i]:
que.pop()
while que and i - que[0] >= k:
que.popleft()
que.append(i)
if i >= k - 1:
res.append(nums[que[0]])
return res
复杂度分析 时间复杂度:O(n) 空间复杂度:O(k)
看的答案,学习了一手单调队列
import collections
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
final_list = []
# 存储位置的单调队列
deque = collections.deque()
for i in range(len(nums)):
while deque and nums[deque[-1]] <= nums[i]:
deque.pop()
while deque and i - deque[0] >= k:
deque.popleft()
deque.append(i)
if i >= k - 1:
final_list.append(nums[deque[0]])
return final_list
时间:O(n) 空间:O(k)
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
const maxSlidingWindow = function(nums, k) {
const n = nums.length;
const res = [];
if (n < k || k === 0) return res;
const deque = []; // monotonic decreasing
for (let i = 0; i < n; i++) {
while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
deque.pop(); // remove all previous elements that are less than nums[i]
}
deque.push(i);
if (i < k - 1) continue; // skips if not reached at least k elements
if (deque[0] <= i - k) deque.shift(); // remove first element if it's at i - k index or less
res.push(nums[deque[0]]);
}
return res;
};
class Solution {
public:
// 简单方法:遍历滑动窗口找最大值,合理选择区间,时间超出限制
vector<int> maxSlidingWindow2(vector<int>& nums, int k) {
vector<int> ret;
if (nums.size() == 0 && k == 0) return ret;
for (int i = 0; i <= nums.size() - k; i++) {
int maxNum = nums[i];
for (int j = i; j < (i + k); j++) {
if (nums[j] > maxNum)
maxNum = nums[j];
}
ret.push_back(maxNum);
}
return ret;
}
// 维护一个单调队列,队头是最大值
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ret;
deque<int> window; // 创建双端队列,单调递减的队列
// 先将第一个窗口的值按照规则入队
for (int i = 0; i < k; i++) {
while (!window.empty() && window.back() < nums[i]) {
window.pop_back();
}
window.push_back(nums[i]);
}
ret.push_back(window.front());
// 模拟滑动窗口的移动
for (int j = k; j < nums.size(); j++) {
if (nums[j - k] == window.front()) window.pop_front(); // 移动后窗口的前一个元素等于队头元素
while (!window.empty() && window.back() < nums[j]) {
window.pop_back();
}
window.push_back(nums[j]);
ret.push_back(window.front());
}
return ret;
}
};
title: "Day 28 239. 滑动窗口最大值" date: 2021-10-07T15:18:45+08:00 tags: ["Leetcode", "c++", "sliding window"] categories: ["91-day-algorithm"] draft: true
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最大值。
示例 1:
输入: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
示例 2:
输入:nums = [1], k = 1
输出:[1]
示例 3:
输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2
输出:[11]
示例 5:
输入:nums = [4,-2], k = 2
输出:[4]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
- 1、滑动窗口题目,最多算个中等题目,可用双端队列解决问题,也可以维护一个 size = k 的最大堆,其中使用双端队列的时间复杂度为O(n),而使用最大堆的时间复杂度为O(nlogn)
- 2、双端队列中维护的只是数组nums中的数组下标,便于
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
deque<int> q;
int n = nums.size();
if(n == 0) return nums;
for(int i = 0; i < n; i++)
{
//如果队头的元素值满足 i - k 的长度时候,即窗口需要向后滑动,将队头的元素踢除!
if(!q.empty() && q.front() == i - k) q.pop_front();
//保证从大到小,如果前面数小则需要依次剔除,直至满足要求
while(!q.empty() && nums[i] > nums[q.back()]) q.pop_back();
q.push_back(i);
if(i >= k - 1) ans.push_back(nums[q.front()]);
}
return ans;
}
};
时间复杂度:O(n)
空间复杂度:O(k)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans;
deque<int> q;
int n = nums.size();
if(n == 0) return nums;
for(int i = 0; i < n; i++)
{
//如果队头的元素值满足 i - k 的长度时候,即窗口需要向后滑动,将队头的元素踢除!
if(!q.empty() && q.front() == i - k) q.pop_front();
//保证从大到小,如果前面数小则需要依次剔除,直至满足要求
while(!q.empty() && nums[i] > nums[q.back()]) q.pop_back();
q.push_back(i);
if(i >= k - 1) ans.push_back(nums[q.front()]);
}
return ans;
}
};
以k 为区间找最大值遍历全部会直接超时。 可以利用双向队列,并保持单调递减方式。队列中其实保证长度只能为k, 这样可以保证队头的元素就是这个k区间最大的值。注意 下一个k区间时要删除前一个k区间的最大值。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
# deque, 存的是index,如果 i - index >= k 那么要popleft()
res = []
queue = collections.deque()
for i,n in enumerate(nums):
while queue and nums[queue[-1]] < n:
queue.pop()
queue.append(i)
if i - queue[0] >= k:
queue.popleft()
if i+1-k >= 0:
res.append(nums[queue[0]])
return res
Time complexity: O(N)
Space complexity: O(k) deque中最多存k个元素
解题方法
- 暴力法,时间复杂度 $O(n-k+1)*k$,这里不知道为什么我写出来的C++代码在leetcode上运行时间超出限制
- 优先级队列(大顶堆)
- 双端队列,构建单调递减队列,队列头是最大值。
class Solution { public: // 简单方法:遍历滑动窗口找最大值,合理选择区间,时间超出限制 vector<int> maxSlidingWindow2(vector<int>& nums, int k) { vector<int> ret; if (nums.size() == 0 && k == 0) return ret; for (int i = 0; i <= nums.size() - k; i++) { int maxNum = nums[i]; for (int j = i; j < (i + k); j++) { if (nums[j] > maxNum) maxNum = nums[j]; } ret.push_back(maxNum); } return ret; } // 维护一个单调队列,队头是最大值 vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int> ret; deque<int> window; // 创建双端队列,单调递减的队列 // 先将第一个窗口的值按照规则入队 for (int i = 0; i < k; i++) { while (!window.empty() && window.back() < nums[i]) { window.pop_back(); } window.push_back(nums[i]); } ret.push_back(window.front()); // 模拟滑动窗口的移动 for (int j = k; j < nums.size(); j++) { if (nums[j - k] == window.front()) window.pop_front(); // 移动后窗口的前一个元素等于队头元素 while (!window.empty() && window.back() < nums[j]) { window.pop_back(); } window.push_back(nums[j]); ret.push_back(window.front()); } return ret; } };
暴力法由于 k 的 值在1 <= k <= nums.length,而 1 <= nums.length <= 10^5, 你这第一种方法约等于o(n^2)的复杂度,一般leetcode设置的不超时的数据规模在10^10以内,都取其最大值,你的算法刚好是(10^5)^2 = 10 ^ 10,,所以暴力法必然是超时的
单调队列,该开始用排序队列结果超时,只能使用第三种单调
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()];
for (int i = k; i < n; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
while (deque.peekFirst() <= i - k) {
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
}
T:O(N) S:O(K)
239. 滑动窗口最大值
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/sliding-window-maximum/
前置知识
题目描述
返回滑动窗口中的最大值。
进阶:
你能在线性时间复杂度内解决此题吗?
示例:
输入: 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 <= nums.length <= 10^5 -10^4 <= nums[i] <= 10^4 1 <= k <= nums.length