LeetCode 滑动窗口最大值

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只能够看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。web

返回滑动窗口最大值。数组

示例:svg

输入: 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
注意:
你能够假设 k 老是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。

进阶:
你能在线性时间复杂度内解决此题吗?
思路分析:使用一个队列做为窗口,顺序储存k个元素。
使用一个双端队列,维持双端队列的递增有序,(队尾为窗口中的最大值)。优化

首先窗口队列的队头出队,以便放入新的元素。
	出队的同时判断出队的元素是否与双端队列的最大值相同,若是相同,双端队列的最大值也须要出队尾
放入一个元素到窗口的队尾。
	入队后将双端队列头部小于刚刚放入的值的元素出队,若是刚刚放入的元素值大于等于当前双端队列的队尾,则这个元素也须要放置到双端队列的队尾
	不然,放入双端队列的对头。(此时双端队列仍然递增有序)
class Solution {
	deque<int> maxEleDeque;//队尾放置当前窗口中的最大值(始终保持递增有序
	queue<int> windowQue;//顺序储存k个元素
public:
	vector<int> maxSlidingWindow(vector<int>& nums, int k) {
		vector<int> result;
		int numsSize = nums.size(), tempValue;
		if (numsSize == 0) {
			return result;
		}

		//第一步:肯定第一个窗口,将前k个值放入队列中
		for (int i = 0; i < k; ++i) {
			windowQue.push(nums[i]);//放入窗口
			//若是放入的值会大于双端队列中的队尾,则双端队列的队尾出队
			while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) {
				maxEleDeque.pop_front();
			}
			//若是当前放入的值大于、等于双端队列的队尾,那么这个值要放入双端队列的对头(维持双端队头为当前窗口中的最大值
			if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) {
				maxEleDeque.push_back(nums[i]);
			}
			else {//不然放入双端队列尾部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增
				maxEleDeque.push_front(nums[i]);
			}
		}
		result.push_back(maxEleDeque.back());//找到的第一个窗口

		//第二步:不断将窗口后移,窗口头部出队,窗口后部入队
		for (int i = k; i < numsSize; ++i) {
			//第一小步:窗口队列先出队头
			tempValue = windowQue.front();
			windowQue.pop();
			//若是出队的值与最大值相等(maxEleDeque队尾
			if (maxEleDeque.back() == tempValue) {
				maxEleDeque.pop_back();//最大值也须要出队
			}
			
			//第二小步:将nums[i]放入窗口队列
			windowQue.push(nums[i]);
			//若是放入的值会大于双端队列中的队尾,则双端队列的队尾出队
			while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) {
				maxEleDeque.pop_front();
			}
			//下面维持双端队列的递增有序
			//第三小步:将双端队列的头部小于nums[i]的元素出队

			//若是当前放入的值大于、等于栈顶,那么这个值要放入队尾(维持队尾为当前队列中的最大值
			if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) {
				maxEleDeque.push_back(nums[i]);
			}
			else {//不然放入双端队列尾部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增
				maxEleDeque.push_front(nums[i]);
			}
			result.push_back(maxEleDeque.back());//当前窗口队列的最大值
		}
		return result;
	}
};

在这里插入图片描述
观察发现,窗口队列有些多余,下面将把窗口队列去掉,将第一个窗口的肯定、窗口的移动进行合并,进行优化。code

class Solution {
	deque<int> maxEleDeque;//队尾放置当前窗口中的最大值(始终保持递增有序
public:
	vector<int> maxSlidingWindow(vector<int>& nums, int k) {
		vector<int> result;
		int numsSize = nums.size(), tempValue;
		if (numsSize == 0) {
			return result;
		}
		//将寻找一个窗口、窗口的移动合并
		for (int i = 0; i < numsSize; ++i) {
			//去掉窗口队列的出队、入队操做
			//只有当窗口中的元素数量等于k,而且出队的元素(nums[i - k])等于队尾最大值时才须要先出队,后入队
			if (i >= k && maxEleDeque.back() == nums[i - k]) {
				maxEleDeque.pop_back();//最大值也须要出队
			}
			//若是放入的值(nums[i])会大于双端队列中的队尾,则双端队列的队尾出队
			while (!maxEleDeque.empty() && maxEleDeque.front() < nums[i]) {
				maxEleDeque.pop_front();
			}
			//下面维持双端队列的递增有序
			//第三小步:将双端队列的头部小于nums[i]的元素出队

			//若是当前放入的值(nums[i])大于、等于队头,那么这个值也要放入队尾(维持栈顶为当前队列中的最大值
			if (maxEleDeque.empty() || nums[i] >= maxEleDeque.back()) {
				maxEleDeque.push_back(nums[i]);
			}
			else {//不然放入双端队列头部(因为上面while循环去除了小于nums[i]的值,因此放入以后双端队列任然有序递增
				maxEleDeque.push_front(nums[i]);
			}
			//只有当窗口中的元素数等于k,才须要寻找最大值
			if (i >= k - 1) {
				result.push_back(maxEleDeque.back());//当前窗口队列的最大值
			}
		}
		return result;
	}
};

在这里插入图片描述