【随记】239.滑动窗口最大值

第一版:

思想:用双指针实现窗口,在每一个窗口找最大值。

如果就这么做出来了,这道题就不是困难了,考虑最坏的情况(比如所有的元素都一样,那么每次都需要初始化max,重新找最大值)时间复杂度O(n^2)

代码:

int main()
{
	vector<int> nums = {1,3,1,2,0,5};
	int k = 3;
	int n = nums.size();
	int start = 0;
	int end = 0;
	int max_ = INT_MIN;
	vector<int> res;//int容器
	while (end < n) {
		max_ = max(max_, nums[end]);
		if (end-start+1==k) {//满足窗口大小
			res.push_back(max_);
			if (max_==nums[start]) {//如果最大值在起始位置,初始化max_,重新找最大值
				max_ = INT_MIN;
				for (int i = start + 1;i <= end;i++) {
					max_ = max(max_, nums[i]);
				}
			}
			start++;
		}
		end++;
	}
	for (int i = 0;i < res.size();i++) {
		cout << res[i] << endl;
	}
}

注意:最大值在起始位置时,窗口滑动后需要重新找最大值,举个例子[5,1,2,3](后面所有例子k=3),第一个窗口max=5,滑动后,如果不重新找max,那么max=5依然比3大,得不到第二个窗口的最大值3,重新找就是在1,2中找到max=2。可能又要问了,为什么要重新找max=2呢?我直接重新初始化max=INT_MIN行不行呢?当然不行。因为我们是用的右指针end作为下标nums[end],换个例子[5,3,1,2],在第二个窗口时,我们只初始化max,end变成3指向第四个元素2,max_ = max(max_, nums[end]);看这条比较语句,发现在第二个窗口直接跳过了3,1直接和2比较了。

第二版:

思想:用一个双端队列来构成一个单调递减队列,队列的头部的元素最大,越往后越小。当向右移动窗口时,新的元素进入窗口,并与队列中的元素进行比较。如果它比队列尾部的元素大,则弹出队列尾部的元素,直到队列为空或新元素小于等于队列尾部的元素。这样可以确保队列头部的元素是窗口中的最大值。同时,如果队列头部的元素超出了窗口范围,也需要将其弹出。

总结:

  1. 对于当前元素,如果其下标与队列头部元素的下标之差等于窗口大小k,表示窗口需要滑动,队列头部元素不应该在当前窗口内,将其从队列头部弹出。例:[3,2,1,0]。ps:这个双端队列只是一个单调递减队列,它里面不一定就一直有窗口大小个数的元素,所以不要想着[1,2,3,0],双端队列弹出头部就把3弹出了,不是这样的,当遍历到2时,2比双端队列里的1大,这一步就会把1弹出。

  2. 对于每个元素,如果其值大于队列尾部元素的值,则从队列尾部依次弹出元素,直到队列尾部元素的值大于等于当前元素的值,再将当前元素的下标插入到队列尾部。

  3. 将当前元素的下标i添加到队列尾部。

  4. 对于每个元素,如果其下标大于等于k-1,即当前窗口大小为k(也可以说窗口开始滑动),将队列头部元素(即当前窗口内最大值的下标对应的元素)的值添加到结果数组中。

代码:

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        // vector<int> nums = { 1,3,-1,-3,5,3,6,7 };
        // int k = 3;
        int n = nums.size();
        int start = 0;
        int end = 0;
        int max_ = INT_MIN;
        vector<int> res;//int容器
	deque<int> dq;
	for (int i = 0; i < n; i++) {
		if (!dq.empty() && dq.front() == i - k) {
			dq.pop_front(); // 弹出队列头部的元素,如果超出了窗口范围
		}
		while (!dq.empty() && nums[dq.back()] < nums[i]) {
			dq.pop_back(); // 弹出队列尾部的元素,直到新元素小于等于队列尾部的元素
		}
		dq.push_back(i); // 将新元素添加到队列尾部
		if (i >= k - 1) {
			res.push_back(nums[dq.front()]); // 队列头部的元素是当前窗口的最大值
		}
	}
        return res;
        }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值