剑指 Offer 59 - I. 滑动窗口的最大值

本文探讨了LeetCode中滑动窗口最大值问题的三种解决方案,包括暴力解法、大根堆实现和双端队列实现。分别分析了它们的时间复杂度和空间复杂度,并给出了每种方法的C++实现。通过比较,展示了双端队列在效率和内存使用上的优势。
摘要由CSDN通过智能技术生成

题目链接:leetcode.

这题明明是困难!
而我只会用暴力,O(nk) ,O(1)

/*
执行用时:148 ms, 在所有 C++ 提交中击败了16.90%的用户
内存消耗:15.2 MB, 在所有 C++ 提交中击败了96.45%的用户 
*/
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(nums.empty())
        	return {};
		int N = nums.size();
		vector<int> ans;
		for(int i = 0;i <= N - k;++i)
		{
			int maxx = INT_MIN;
			for(int j = i;j < i + k;++j)
			{
				maxx = max(maxx, nums[j]);
			}
			ans.emplace_back(maxx);
		}
		return ans;
    }
};

· 初始时,我们将数组 nums 的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
· 我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。
· 为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组(num,index),表示元素 num 在数组中的下标为 index

时间复杂度:O(nlogn) 。优先队列(大根堆)插入的时间复杂度是logn
空间复杂度:O(n)

/*
执行用时:24 ms, 在所有 C++ 提交中击败了75.04%的用户
内存消耗:16.5 MB, 在所有 C++ 提交中击败了8.06%的用户
*/
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(nums.empty())
        	return {};
		int N = nums.size();
		vector<int> ans;
		priority_queue< pair<int, int> > Q; //priority要拼对 
		for(int i = 0;i < k;++i)
		{
			Q.push({nums[i], i});
		}
		ans.push_back(Q.top().first);
		for(int i = k;i < N;++i)
		{
			Q.push({nums[i], i});
			while(Q.top().second <= i - k)
			{
				Q.pop();
			}
			ans.push_back(Q.top().first);
		}
		return ans;
    }
};

· 使用双端队列,里面按序存储数组的下标,当新来的j对应的元素nums[j]大于队尾中已有的nums[i]时,nums[i]就不可能是当前窗口的最大值了,将这样的下标 i 从队尾弹出
· 且队首的下标应该大于等于j - k (滑动窗口要有效),将失效的下标从队首弹出

O(n), O(k)

/*
执行用时:16 ms, 在所有 C++ 提交中击败了97.22%的用户
内存消耗:15.7 MB, 在所有 C++ 提交中击败了46.91%的用户
*/
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(nums.empty())
        	return {};
		int N = nums.size();
		vector<int> ans;
		deque<int> q;
		for(int i = 0;i < k;++i)
		{
			while(!q.empty() && nums[i] >= nums[q.back()])
			{
				q.pop_back();
			}
			q.push_back(i);
		}
		ans.emplace_back(nums[q.front()]);
		for(int i = k;i < N;++i)
		{
			while(!q.empty() && nums[i] >= nums[q.back()])
			{
				q.pop_back();
			}
			q.push_back(i);
			while(!q.empty() && q.front() <= i - k)
			{
				q.pop_front();
			}
			ans.emplace_back(nums[q.front()]);
		} 
		return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值