力扣-239-滑动窗口的最大值

居然是道困难题,与之相比,子序列只是道简单题

拆分成两个步骤:

  1. 维持滑动窗口
  2. 找出窗口中的最大值

那么能不能将这两个步骤统一呢?

考虑每次都是取走滑动窗口的第一个元素,新插入最后一个元素

  • 如果取走元素不是最大元素,这个好说,maxNum = max(maxNum,newNum)
  • 如果取走的就是maxNum,如果没有相同的数字还好说,但是如果窗口中有不止一个maxNum呢?也就是最大值重复的情况
// 不得已扫描一遍整个序列,找出最大值
int findMax(vector<int>& nums, int left, int right) {
	int maxNum=nums[left];
	for (int i = left+1; i <= right; i++) 
		maxNum = max(maxNum, nums[i]);
	return maxNum;
}

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
	vector<int> res(1);
	
	for (int i = 0; i < nums.size(); i++) {
		// 初始化滑动窗口
		if (i < k) res[0] = max(res[0], nums[i]);
		else {
			if (nums[i] >= res[i-k]) res.push_back(nums[i]);
			else {
				// 如果被丢出去的是最大值,但是没法保证剩下的里面没有重复的最大值,所以只能扫描一遍
				if (nums[i-k] = res[i - k]) res.push_back(findMax(nums, i-k+1, i));
				// 被丢出去的不是最大值,新加入的又没有最大值大,那最大值仍旧是最大值
				else res.push_back(res[i - k]);
			}
		}
	}
	return res;
}

这是我的第一版代码,很直接的思路,我也知道那个全序列扫描绝对是瓶颈,测试用例也不负所望,给了k=5000的不当人用例,果然难和40几的通过率不是白来的

可能得换个思路才行了

我想到了二维动态规划

	// 定义dp[i][j]表示从第i个元素开始,到第j个元素为止的最大值
	// 状态转移方程为:横向:dp[i][j] = max(dp[i][j-1],nums[j])
	// 纵向为:dp[i][j]=

好像也不对,这么定义的话这是一个上三角,而且也多了很多冗余

官解

优先队列…和单调队列
都听过见过,但也仅限于此
优先队列就是堆,那么它和普通队列有什么区别?
单调队列以前遇到过一次,具体哪个题忘记了

优先队列解法

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
	vector<int> res;
	priority_queue<pair<int,int>> window;
	// 好,既然会用优先队列了,就用优先队列解决这个问题
	// 但问题是,它改变了队列的原始顺序,我怎么pop第一个元素呢?并不能pop指定元素
	// 什么意思?其实并不需要每一个都pop,如果本应pop的元素不是最大值,因为我们只关心最大值,所以pop与否没有影响
	// 但是如果待pop元素就是最大值,那么直接pop堆顶就可以了
	// 这也是题目提示中的意思,这个队列的大小并不需要是固定的
	for (int i = 0; i < nums.size(); i++) {
		window.emplace(nums[i],i);

		if (i >= k-1) {
			while (window.top().second < i - k + 1) window.pop();
			res.push_back(window.top().first);
		}
	}
	// 不行,这里不pop其实是对后面有影响的,必须有个机制在某个时间将左边界左侧的元素排除
	// 实现这一点是通过保存一个额外的索引来实现的
	// 这个时机是在res取之前,保证这个max确实是在窗口中的
	return res;
}

空间效率很不理想,时间效率也不算好
根本上是来自于堆的空间占用和堆的调整用时上面,虽然不用自己实现,但是过程消耗是真实存在的

堆排序时间复杂度O(n logn),空间复杂度O(1)

单调队列解法

我去看了单调队列,说实话没看明白,等二刷吧

posted @ 2022-11-23 11:57  YaosGHC  阅读(39)  评论(0)    收藏  举报