【二叉树】【单调双向队列】LeetCode239:滑动窗口最大值

作者推荐

map|动态规划|单调栈|LeetCode975:奇偶跳

涉及知识点

单调双向队列 二叉树

题目

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

返回 滑动窗口中的最大值 。

示例 1:

输入: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

示例 2:

输入:nums = 1, k = 1

输出:1
参数范围

1 <= nums.length <= 105

-104 <= numsi <= 104

1 <= k <= nums.length

单调栈

时间复杂度 😮(n)。

queMax中记录(i-k,i],如果i1 < i2,且numsi1 <=numsi2,那么i1无论如何都无法成为最大值。故可以淘汰i1,淘汰i1后,成降序排列。队首元素最大。

对queMax有三种操作。

操作一 队尾淘汰i1
操作二 队尾插入i2
操作三 队首删除i-k,由于操作二,queMax不会为空,所以无需判断是否为空。如果i-k已经被操作一淘汰,则不能删除。

代码

核心代码

cpp 复制代码
class Solution {
public:
	vector<int> maxSlidingWindow(vector<int>& nums, int k) {
		int i = 0;
		std::deque<int> queMax;
		vector<int> vRet;
		for ( i = 0; i < k; i++)
		{
			while (queMax.size() && (nums[queMax.back()] <= nums[i]))
			{
				queMax.pop_back();
			}
			queMax.emplace_back(i);			
		}
		vRet.emplace_back(nums[queMax.front()]);
		for (; i < nums.size(); i++)
		{
			if (i - k == queMax.front())
			{
				queMax.pop_front();
			}
			while (queMax.size() && (nums[queMax.back()] <= nums[i]))
			{
				queMax.pop_back();
			}
			queMax.emplace_back(i);
			vRet.emplace_back(nums[queMax.front()]);
		}
		return vRet;
	}
};

测试用例

cpp 复制代码
template<class T>
void Assert(const T& t1, const T& t2)
{
	assert(t1 == t2);
}

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
	if (v1.size() != v2.size())
	{
		assert(false);
		return;
	}
	for (int i = 0; i < v1.size(); i++)
	{
		Assert(v1[i], v2[i]);
	}
}


int main()
{
	vector<int> nums;
	int k;
	{
		Solution sln;
		nums = { 1, 3, -1, -3, 5, 3, 6, 7 }, k = 3;
		auto res = sln.maxSlidingWindow(nums, k);
		Assert(vector<int>{ 3,3,5,5,6,7 }, res);
	}
	{
		Solution sln;
		nums = { 1 }, k = 1;
		auto res = sln.maxSlidingWindow(nums, k);
		Assert(vector<int>{ 1 }, res);
	}

//CConsole::Out(res);
}

2023年3月二叉树

用多键二叉树(红黑树)mulset记录滑动窗口中的数,由于二叉树默认是升序排列,所以最后一个元素,就是最大值。由于二叉树的插入、删除的时间复杂度是O(logn),故总时间复杂度是O(nlogn)

cpp 复制代码
 class Solution {
 public:
	 vector<int> maxSlidingWindow(vector<int>& nums, int k) {
		 int i = 0;
		 std::multiset<int> setNums;
		 for (; i + 1 < k; i++)
		 {
			 setNums.insert(nums[i]);
		 }
		 vector<int> vRet;
		 for (; i < nums.size(); i++)
		 {
			 setNums.insert(nums[i]);
			 vRet.push_back(*setNums.rbegin());
			 auto it = setNums.find(nums[i + 1 - k]);
			 setNums.erase(it);
		 }
		 return vRet;
	 }
 };

2023年3月第二版

class Solution {

public:

vector maxSlidingWindow(vector& nums, int k) {

vector<pair<int, int>> vValueIndex;

vector vRet;

int iPos = 0;

for (int i = 0; i < nums.size(); i++)

{

while ( ( vValueIndex.size() > iPos ) && (numsi >= vValueIndex.back().first))

{

vValueIndex.pop_back();

}

vValueIndex.emplace_back(numsi, i);

if (i + 1 >= k)

{

vRet.push_back(vValueIndexiPos.first);

}

if (i + 1 - k == vValueIndexiPos.second)

{

iPos++;

}

}

return vRet;

}

};

2023年8月版

class Solution{

public:

vector maxSlidingWindow(vector&nums, int k) {

m_c = nums.size();

//每k个元素用一组,vLeft各元素到组首的最大值,vRight各元素到组尾的最大值

vector vLeft(m_c), vRight(m_c);

int iMax = 0;

for (int i = 0; i < m_c; i++)

{

if (0 == i % k)

{

iMax = numsi;

}

else

{

iMax = max(iMax, numsi);

}

vLefti = iMax;

}

iMax = -100 * 1000;

for (int i = m_c-1;i >= 0 ; i-- )

{

if (0 == (i+1) % k)

{

iMax = numsi;

}

else

{

iMax = max(iMax, numsi);

}

vRighti = iMax;

}

vector vRet;

for (int i = k-1; i < m_c; i++)

{

vRet.emplace_back( max(vRighti-k+1, vLefti));

}

return vRet;

}

int m_c;

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法C++ 实现。

相关推荐
哥不想学算法2 小时前
【C++】字符串字面量拼接
开发语言·c++
海石2 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石2 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry4 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding5 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL6 小时前
我的数据结构3——链表(link list)
数据结构·链表
a1117767 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜7 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry7 小时前
LeetCode 541. 反转字符串 II
算法
Jerry7 小时前
LeetCode 344. 反转字符串
算法