leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 28 】2021-10-07 - 239. 滑动窗口最大值 #45

Open azl397985856 opened 2 years ago

azl397985856 commented 2 years ago

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

HackBL commented 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)

sxr000511 commented 2 years ago

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; };

shamworld commented 2 years ago

代码

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
};
chakochako commented 2 years ago
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
RocJeMaintiendrai commented 2 years ago

题目

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)

ymkymk commented 2 years ago

思路

优先队列

代码

``

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)

user1689 commented 2 years ago

题目

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

思路

bruteForce, minStack

python

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

时间复杂度

相关题目

  1. https://leetcode-cn.com/problems/minimum-window-substring/
watermelonDrip commented 2 years ago
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
flame0409 commented 2 years ago

思路:

使用单调队列。

窗口在划动时,其实只有最左边的元素出去,和最右边的元素进来。所以我们可以考虑使用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)

xjlgod commented 2 years ago
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;
    }
}
joeytor commented 2 years ago

思路

维持一个单调队列, 这样第一个数 一定是 此时窗口的最大值

每次有一个新的数, 那么如果这个数比队尾的数大, 就一直 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 个元素

Dana-Dai commented 2 years ago
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;
    }
};
biancaone commented 2 years ago

思路 🏷️

保持一个单调递减的双端队列


代码 📜

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

复杂度 📦

zol013 commented 2 years ago

思路: 创建一个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)

shixinlovey1314 commented 2 years ago

Title:

Question Reference LeetCode

Solution

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.

Code

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;
    }
};

Complexity

Time Complexity and Explanation

O(n), since each key would only enter and exit the list once, it only takes O(n)

Space Complexity and Explanation

O(k), used for the list

biscuit279 commented 2 years ago

思路: 单调队列

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)

Daniel-Zheng commented 2 years ago

思路

单调栈。

代码(C++)

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;
    }
};

复杂度分析

XinnXuu commented 2 years ago

思路

严格单调递减的双端队列。当新元素比队尾元素大时,位置在它之前的且值比它小的元素就失去了意义无需储存。

代码

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;
    }
}

复杂度分析

ai2095 commented 2 years ago

LC239. Sliding Window Maximum

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

Topics

Similar Questions

Hard

Medium

思路

单调栈 and deque

代码 Python

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)

muimi commented 2 years ago

思路

单调栈

代码

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;
  }
}

复杂度

florenzliu commented 2 years ago

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:

Tao-Mao commented 2 years ago

Idea

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.

Code

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;
    }
}

Complexity

Bochengwan commented 2 years ago

思路

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

复杂度分析

carsonlin9996 commented 2 years ago

用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;
    }
}
leolisgit commented 2 years ago

思路

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)

Yufanzh commented 2 years ago

Intuition

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

  1. add element: push()
  2. remove element: pay attention to two conditions a. if tail element is smaller than curElement, needs to pop tails out until queue is decreasing b. if head element is not inside sliding window, pop out head element

Algorithm in python3

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

Complexity Analysis:

mannnn6 commented 2 years ago

思路

单调队列

代码

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)

taojin1992 commented 2 years ago

Understand:

1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length

k == 1, nums
k > nums.length, global max

Plan:

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)

Code:

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;
    }

}
qyw-wqy commented 2 years ago
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)

Huangxuang commented 2 years ago

题目:239. Sliding Window Maximum

思路

代码

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;
    }
}

复杂度分析

Cartie-ZhouMo commented 2 years ago

思路

利用单调队列保存元素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

复杂度

15691894985 commented 2 years ago

[Day 28] (239. 滑动窗口最大值)

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

思路:要求线性时间复杂度,使用单调递增栈,窗口每次向右移动,窗口最左侧的元素需要被擦除,因此用双端队列保存滑动窗口可能成为最大的值

cassiechris commented 2 years ago

思路

学习题解思路:单调队列

代码

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

复杂度分析

ginnydyy commented 2 years ago

Problem

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

Notes

Solution

Complexity

wangyifan2018 commented 2 years ago
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
}
zhiyuanpeng commented 2 years ago
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)

ysy0707 commented 2 years ago

思路:队列+滑动窗口

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)

Zhang6260 commented 2 years ago

JAVA版本

思路:使用单调栈来完成。维护一个单调递增的栈,值得注意的是如果上次移除的数刚好是最大的数,则需要将栈的第一个数手动移除。

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;

   }

}

时间复杂度:O(n)

空间复杂度:O(k)

ZJP1483469269 commented 2 years ago

思路

优先级队列模拟大顶堆

代码

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)

potatoMa commented 2 years ago

思路


使用单调递减队列维护当前窗口的最大值,窗口离开该最大值索引时弹出队列

代码


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)

miss1 commented 2 years ago
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;
}
Laurence-try commented 2 years ago

思路

单调队列

代码

使用语言: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)

Toms-BigData commented 2 years ago

【Day 28】239. 滑动窗口最大值

思路

看的答案,学习了一手单调队列

Python代码

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)

kennyxcao commented 2 years ago

239. Sliding Window Maximum

Intuition

Code

/**
 * @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;
};

Complexity Analysis

HarleysZhang commented 2 years ago

解题方法

  1. 暴力法,时间复杂度 $O(n-k+1)*k$,这里不知道为什么我写出来的C++代码在leetcode上运行时间超出限制
  2. 优先级队列(大顶堆)
  3. 双端队列,构建单调递减队列,队列头是最大值。
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;
    }
};
chaggle commented 2 years ago

title: "Day 28 239. 滑动窗口最大值" date: 2021-10-07T15:18:45+08:00 tags: ["Leetcode", "c++", "sliding window"] categories: ["91-day-algorithm"] draft: true


239. 滑动窗口最大值

题目

给你一个整数数组 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;
    }
};

复杂度

yukicoding commented 2 years ago
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;
    }
};
Venchyluo commented 2 years ago

以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个元素

chaggle commented 2 years ago

解题方法

  1. 暴力法,时间复杂度 $O(n-k+1)*k$,这里不知道为什么我写出来的C++代码在leetcode上运行时间超出限制
  2. 优先级队列(大顶堆)
  3. 双端队列,构建单调递减队列,队列头是最大值。
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,,所以暴力法必然是超时的

HouHao1998 commented 2 years ago

思想

单调队列,该开始用排序队列结果超时,只能使用第三种单调

代码

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)