滑动窗口系列题目

滑动窗口的最大值

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入
数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值
分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: 
{[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, 
{2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:普通做法是暴力遍历,时间复杂度为O(k*n).  若想用更优的时间复杂度,需要用一个单调队列求解。滑动窗口向右滑,数组中的元素对于滑动窗口最大值的影响可以这样分析:
越靠右的越有用,因为窗口是向右滑的,所以数组左边元素总是先用到并且先被丢弃,右边的元素总是后用到;
值越大越有用,因为求的是区间内的最大值。
所以,把当前窗口包含的所有元素从左到右依次加入队列,并且丢掉那些既在左边而且值还更小的元素,以及那些已经出了窗口范围的元素。
更具体的证明:假设队列中有两个元素 a 和 b ,  a<b且 a 比 b 先入队。那么对于当前窗口,其最大值肯定不是 a, 因为 a<b; 同样的道理,对于后面的每个同时包含这两个元素的窗口,它们的区间最大值也都不可能是 a。并且 a 是先入队的,所以在窗口滑动的过程中, a 也肯定是先出窗口范围的。综上,a 在它的生命周期中永远被 b 压一头,对任何窗口(指b进了范围之后的窗口)的最大值都不会造成影响。

因为需要从队列的两端都删除元素,所以用双端队列deque实现该算法(注意队列中存放的是元素的下标序号,而非元素本身):

vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
	deque<int> dque;
	vector<int> vec;
	for (int i = 0; i<num.size(); i++) {
        //从back删除和插入
		while ((!dque.empty()) && num[dque.back()] <= num[i]) 
			dque.pop_back();			
		dque.push_back(i);
        //从front读取和删除
		if (i >= size - 1) 
			vec.push_back(num[dque.front()]);			
		if (dque.front() <= i - (int)size + 1) //这里的size必须显式转换成int型
			dque.pop_front();			
	}
	return vec;
}

猜你喜欢

转载自blog.csdn.net/sinat_38972110/article/details/82954948