OJ滑动窗口最大值

 

题目

给定一个数组 nums, 有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧

你只可以看到在滑动窗口内的 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

思路

此处使用双向队列(deque)这个数据结构

当前滑动窗口最大值总是保存在队头, 队列里数据从大到小排列

若遇到值比队尾元素大, 则将从队尾开始比该值小的元素出队, 再将新值插入队尾中

若遇到值比当队尾元素小, 则直接插入到队尾

每次移动时, 判断队头元素(窗口最大值)是否在有效范围, 若不在, 将其从队列中删除

num = [1, 3, -1, -3, 5, 3, 6, 7], k = 3

原始数组 队列 res
1 3 -1 -3 5 3 6 7 1  
1 3 -1 -3 5 3 6 7 3  
1 3 -1 -3 5 3 6 7 3, -1 3
1 3 -1 -3 5 3 6 7 3, -1, -3 3, 3
1 3 -1 -3 5 3 6 7 5 3, 3, 5
1 3 -1 -3 5 3 6 7 5, 3 3, 3, 5, 5
1 3 -1 -3 5 3 6 7 6 3, 3, 5, 5, 6
1 3 -1 -3 5 3 6 7 7 3, 3, 5, 5, 6, 7
原始数组 队列 res
2 3 4 2 6 2 5 1 2  
2 3 4 2 6 2 5 1 3  
2 3 4 2 6 2 5 1 4 4
2 3 4 2 6 2 5 1 4, 2 4, 4
2 3 4 2 6 2 5 1 6 4, 4, 6
2 3 4 2 6 2 5 1 6, 2 4, 4, 6, 6
2 3 4 2 6 2 5 1 6, 5 4, 4, 6, 6, 6
2 3 4 2 6 2 5 1 5, 1 4, 4, 6, 6, 6, 5

最后一行, 队头6由于已经不在窗口范围内故删除

代码

// v:待处理元素
// w:窗口大小
vector<int> maxSlideWindow(vector<int> v, int w) {
    vector<int> res;
    // q中储存的是v中元素的下标
    deque<int> q;
    
    // 处理前几个元素
    for (int i = 0; i < w; i++) {
        while (!q.empty() && v[i] >= v[q.back()]) {
            q.pop_back();
        }
        q.push_back(i);
    }
    for (int i = w; i < v.size(); i++) {
        // 队头元素为当前窗口最大值
        res.push_back(v[q.front()]);
        while
        // 当前元素大于队尾元素时删除队尾元素
        while (!q.empty() && v[i] >= v[q.back()]) {
            q.pop_back();
        }
        //当队头元素(最大值)不在窗口范围内时删除队头元素
        while (!q.empty() && q.front() <= i - w) {
            q.pop_front();
        }
        q.push_back(i);
    }
    res.push_back(v[q.front()]);
    return res;
}

补充

个人感觉难点在于删除队头元素(最大值)这一部分, 下面在进行说明

对于第i个元素, 若其是当前窗口的最大值, 那么其有效的范围为[i-w+1, i]

元素 : 2 3 4 2 6 2 5 1

下标 : 0 1 2 3 4 5 6 7

从第三个元素4开始:

  • i = 2, v[2] = 4 ; i-w = -1 ; 有效范围:[0, 2] ; q.front = 2 ;q = [2]

  • i = 3, v[3] = 2 ; i-w = 0 ; 有效范围:[1, 3] ; q.front = 2 ; q = [2, 3]

  • i = 4, v[4] = 6 ; i-w = 1 ; 有效范围:[2, 4] ; q.front = 4 ; q = [4]

  • i = 5, v[5] = 2 ; i-w = 2 ; 有效范围:[3, 5] ; q.front = 4 ; q = [4, 5]

  • i = 6, v[6] = 5 ; i-w = 3 ; 有效范围:[4, 6] ; q.front = 4 ; q = [4, 6]

  • i = 7, v[7] = 1 ; i-w = 4 ; 有效范围:[5, 7] ; 原本队列q = [4, 6, 7], 原本队头q.front = 4 <= i-w 不在有效范围内, 故出队;新队列q = [5, 6, 7], 新队头q.front = 6