题目如下:
拿到手只想到了暴力解法,就是每次移动窗口,移动之后再查看一下当前窗口里的最大值。
时间复杂度较大,为O(n*k) k为移动窗口大小。
代码如下:
class Solution {//参考牛客官方解答code
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> myret;
if (num.size() == 0 || size < 1 || num.size() < size) return myret;
//判断条件,当num的大小等于0或者滑动窗口的size小于1或者滑动窗口移动到数组最后面时
//以上这些情况都会直接返回结果
int n = num.size()
for (int i = 0; i + size - 1 < n; ++i) {
int j = i + size - 1;
int max_val = num[j];
for (int k = i; k < j; ++k) {
max_val = max(max_val, num[k]);//在滑动窗口中进行判断最大值
}
myret.push_back(max_val);//保存最大值,循环完之后输出结果。
}
return myret;
}
};
第二种方法就是觉得每次比较所有元素太啰嗦了
这个方法总的来说就是只让双端队列保存最大元素
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size) {
if (num == null || num.length == 0 || size <= 0 || num.length < size) {
return new ArrayList<Integer>();
}
ArrayList<Integer> result = new ArrayList<>();
//双端队列,用来记录每个窗口的最大值下标
LinkedList<Integer> qmax = new LinkedList<>();
for (int i = 0; i < num.length; i++) { //这个for循环用来遍历每个点
//这个循环用来比较当前遍历元素和之前所有元素,比当前元素小的就直接扔掉,不配当滑动窗口最大值
while (!qmax.isEmpty() && num[qmax.peekLast()] < num[i]) {
qmax.pollLast(); }
qmax.addLast(i); //每次必须加进去
//判断队首元素是否过期 就是看这个元素是否被滑动窗口滑过去了
if (qmax.peekFirst() <= i - size) {
qmax.pollFirst();
}
//向result列表中加入元素 以上所有操作都做完说明当前队列队首的就是最大元素
if (i >= size - 1) { //只有当当前索引超过了滑动窗口大小才可以往里面塞东西
result.add(num[qmax.peekFirst()]);
}
}
return result;
}
}
它用了一个双端队列,就是可以查看队列队首元素与队尾的一个数据结构。
首先是对每个元素的遍历循环:
- 在这个循环里,第一步就是查看刚才队列里的元素和当前元素作比较,做一个剔除操作(这个队列最大就只有size个元素,所以不用担心剔除的时候把什么重要的删掉)
- 之后就是将当前元素加入队列
- 查看一下队首元素是否过期了
- 将队首元素放入结果中 (注意只有遍历的节点索引超过了滑动窗口才可以放 不然放的都是没比较过的东西)
就这个方法看起来挺聪明的,但是好像也就那样没啥卵用。时间复杂度还是不低。
但是并不是这样 因为维护单调队列的过程中是线性的遍历一遍元数组(就是那个剔除操作,剔除的时候只会看一遍那个元素,不可能遍历好多遍,因为那个元素要么太小被扔要么滑出去了,反正比较次数特别小),每个元素只被访问一次,故复杂度为O(n)