剑指offer 59-滑动窗口的最大值&队列的最大值 C++

一、滑动窗口的最大值

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。

思路1:蛮力法,扫描每个滑动窗口所有数字并找出其中的最大值。如果滑动窗口的大小为k,则需要O(k)的时间复杂度找出最大值;对于长度为n的数组,总时间复杂度为O(nk).

class Solution {
public:
	vector<int> maxInWindows(const vector<int>& num, unsigned int size)
	{
		vector<int> res;
		if (num.size() == 0 || size == 0) return res;
		for (int i = 0; i < num.size()-size+1; i++) {   //注意num.size()-size+1
			int j = i;
			int count = 0;
			vector<int> arr;
			while (count!=size)
			{
				arr.push_back(num[j++]);
				count++;
			}
			//sort(arr.begin(), arr.end());   //排序,时间复杂度为O(klogk)
			//res.push_back(arr.back()); 
			int max = 0;    
			for (auto i : arr) if (max < i) max = i;   //该方法时间复杂度为O(k)
			res.push_back(max);    //取出滑窗中的最大值
		}
		return res;
	}
};

思路2:只把有可能成为滑动窗口最大值的数值的下标存入一个双端队列,队首存放当前滑窗最大值的下标。如果队首和队尾的下标之差大于等于滑窗大小时,就将队首删除。

class Solution {
public:
	vector<int> maxInWindows(const vector<int>& num, unsigned int size)
	{
		vector<int> res;
		if (num.size() == 0 || size == 0 || size > num.size()) return res;
		deque<int> dq;
		for (int i = 0; i < size; i++) {  //先构造窗口大小
			if (!dq.empty() && num[i] >= num[dq.back()]) {  //若当前值大于等于队尾,则删除队尾,否则就将当前值存入队尾
				dq.pop_back();
			}
            dq.push_back(i);
		}
		res.push_back(num[dq.front()]);  //存入第一个满足要求的最大值

		for (int i = size; i < num.size(); i++) {
			while (!dq.empty() && num[i] >= num[dq.back()]) {  //若当前值大于等于队尾,则删除队尾,否则就将当前值存入队尾
				dq.pop_back();
			}
			dq.push_back(i);
			if (dq.back() - dq.front() >= size)   //若队首元素与队尾元素相差大于等于滑窗大小,则删除队首
				dq.pop_front();
			res.push_back(num[dq.front()]);
		}
		return res;
	}
};

也可以写成:

class Solution {
public:
	vector<int> maxInWindows(const vector<int>& num, unsigned int size)
	{
		vector<int> res;
		if (num.size() == 0 || size == 0) return res;
		deque<int> dq;

		for (int i = 0; i < num.size(); i++) {
			while (!dq.empty() && num[i] >= num[dq.back()]) {
				dq.pop_back();
			}
			dq.push_back(i);
			if (dq.back() - dq.front() >= size)
				dq.pop_front();
			if(i+1>=size)   //当滑动窗口首地址i大于等于size时才开始写入窗口最大值
				res.push_back(num[dq.front()]);
		}
		return res;
	}
};
二、队列的最大值

题目描述:
请定义一个队列并实现函数max得到队列里的最大值,要求函数max、push_back和pop_front的时间复杂度都是O(1)。

思路:使用两个队列,一个普通队列用于存放输入元素,一个双端队列用于存放当前队列最大值以及之后可能的最大值,保持从队首到队尾是递减的。队头存放最大值,队尾为之后可能的最大值

#include<iostream>
#include<string>
#include<queue>
using namespace std;

class Solution {
public:
	void push_back(int num) 
		data.push(num);
		while (!maxvalue.empty() && num > maxvalue.back())
			maxvalue.pop_back();
		maxvalue.push_back(num);		
	}

	int pop_front() {
		if (data.empty())
			return -1;
		int front = data.front();
		data.pop();
		if (!maxvalue.empty() && maxvalue.front() == front)
			maxvalue.pop_front();
		return front;
	}

	int max() {
		if (maxvalue.empty()) return -1;
		return maxvalue.front();
	}
private:
	queue<int> data;
	deque<int> maxvalue;
};

void test() {
	Solution s;
	s.push_back(7);   //data:7
	cout << s.max() << endl;    //输出7
	s.push_back(6);  //data:7,6
	s.pop_front();   //删除7
	s.pop_front();   //删除6
	cout << s.max() << endl;   //输出-1
	s.push_back(4);    //data:4
	cout << s.max() << endl;   //输出4
	s.push_back(5);    //data:4,5     5比4大,此时把队首4删除
	s.pop_front();   
	cout << s.max() << endl;  //输出5
	s.push_back(3);  //data:5,3
	s.pop_front();  //删除5
	cout << s.max() << endl;  //输出3
	s.push_back(2);  //data:3,2
	cout << s.max() << endl;  //输出3
}

int main() {
	test();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值