剑指offer59. 队列中的最大值 P288
1. 滑动窗口的最大值 P288
题目:给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。例如,如果输入数组{2, 3, 4, 2, 6, 2, 5, 1}及滑动窗口的大小3,那么一共存在6个滑动窗口,它们的最大值分别为{4, 4, 6, 6, 6, 5},
通常,er会这样考虑:记录当前窗口的最大值,然后移动的时候把最大值和下一个进入窗口的元素比较取最大的就好,但是如果最大值划出窗口了,拿什么比较呢?是不是应该拿上一个窗口第二大的元素和这个刚进入窗口的元素比较呢? 这样就只用一个数据结构同时保存窗口最大,次大值(这两个就是书上所说的:有可能成为下一个窗口最大值的元素)就好了。
可以用双端队列。
每次读入一个新值,首先看最大值是不是已经划出窗口,如果是,把次大值和新值比较,更新队列,不是,把最大值比较新值,更新队列。因为要比较上一个最大值是不是划出窗口,所以队列存的是索引。用i - dq_index.front() >= k判别当前最大是不是划出窗口了。队头存的是当前窗口的最大值的索引,(不要误认为队列中元素个数保持<=2,不管是否更新队列,都把当前元素入队)
vector<int> maxInWindows(const vector<int> nums, int k) { // k是窗口大小
vector<int> v; // 返回最大值序列
if (nums.size() < k || k < 1 || nums.size() < 1) return v;
deque<int> dq_index; // 双端队列放的是索引,
// 用i - dq_index.front() >= k判别当前最大是不是划出窗口了
for (int i = 0; i < k; ++i) { // 处理第一个窗口
while (!dq_index.empty() && nums[i] > nums[dq_index.back()])
dq_index.pop_back();
dq_index.push_back(i);
}
v.push_back(nums[dq_index.front()]);
for (int i = k ; i < nums.size(); ++i) { // 从第k个值到结尾
while (!dq_index.empty() && nums[i] > nums[dq_index.back()])
dq_index.pop_back(); // 当前值比队列尾部元素大,弹出尾部
if (!dq_index.empty() && i - dq_index.front() >= k ) //队头划出窗口
dq_index.pop_front();
dq_index.push_back(i); // 不管是否更新队列,都把当前元素入队
v.push_back(nums[dq_index.front()]);
}
return v;
}
2. 队列的最大值 P292 (实现一个队列,并在O(1)时间找出最大的队列元素)
template<typename T> class QueueWithMax {
private :
struct InternalData{
int index; // 出队时比较索引大小,以此更新max_dq;
T number; // 入队时比较数据大小
};
int index;
queue <InternalData> data_dq;
deque <InternalData> max_dq;
public:
QueueWithMax(){ index = 0; };
~QueueWithMax(){};
void push_back(T num) {
InternalData data; // 不可以new
data.index = index;
data.number = num;
++index;
data_dq.push(data);
// 下面3行和上面滑动窗口一样 ,插入最大的
while (!max_dq.empty() && max_dq.back().number < num)
max_dq.pop_back();
max_dq.push_back(data);
}
void pop_front() {
if (data_dq.empty()) throw "queue is empty!";
// 出队的时候,要用索引判断data队头是不是当前最大的,是的话当前max也要出队
if (data_dq.front().index == max_dq.front().index) {
max_dq.pop_front();
}
data_dq.pop();
}
// 因为测试Test函数形参声明模板类是const QueueWithMax<int>& queue
// 一个const对象不能调用它的non-const成员函数
// const在成员函数右边表示不能修改成员变量
// 前面两个void函数在测试中是在main中直接调的,queue没有用const修饰
T max() const{
if (max_dq.empty()) throw "queue is empty!";
return max_dq.front().number;
}
};