解一(暴力解):假设窗口为k,数组大小为n,每次在一个窗口找最大值,遍历n-k次,则时间复杂度O(kn)
优解:时间消耗大的还是在找最大值方面,定义一个单调(从大到小单调减)队列,队列里面仅维护当前窗口可能的最大值。时间复杂度O(n)
单调队列遵循规则:
pop():队列非空,且并且当前value值等于单调队列的队头元素,则弹出队头元素
push(value):value值必须大于队尾元素,才把value加入单调队列,若不是,则一直弹出队尾元素直到队列为空或满足value大于队尾元素.再加入队列。
核心可以理解为:滑动窗口删除(pop)前一个元素,后加入(push)后一个元素可以实现滑动,因为队列仅保存当前窗口可能的最大值(并不是全保存,若当前队列中的值比新加入的元素--push进来的元素还小,则没必要再维护这些小值,因为迟早会被舍弃),数组前窗口删除的元素如果和队头元素相等就应该pop出来,保持队中元素和当前窗口元素的一致性。push则是把后窗口新加入的元素和队中元素比较,把队列中比新加入的元素还小的直接删除。以上就是对该解法比较核心的理解
代码:
class Solution {
class MyQueue{//重新定义队列方法,单调递减
public:
deque que;
void pop(int value){
if(!que.empty() && value==que.front()){//仅队列非空且队头元素和窗口最前面的移除值相同才pop
que.pop_front();
}
}
void push(int value){
while(!que.empty() && value>que.back()){//仅队列非空且要push的元素比队尾元素大,则把队尾元素移除
que.pop_back();
}
que.push_back(value);//此时push,前面的元素都不会比当前元素大
}
int front(){
return que.front();
}
};
public:
vector maxSlidingWindow(vector& nums, int k) {
vector result;
MyQueue que;//队列存放当前窗口可能的最大值
//1,初始第一个窗口
for(int i = 0;i < k;i++){//先push数组前k个元素
que.push(nums[i]);
}
result.push_back(que.front());//再统计下当前窗口的最大值
//2,窗口滑动
for(int i = k;i < nums.size();i++){
//i记录尾部位置,i-k记录头部位置
que.pop(nums[i-k]);//滑动窗口移动前先删除最前面的元素
que.push(nums[i]);//上面删除最前面的元素后,再加入后面的一个元素,实现滑动
result.push_back(que.front());//记录当前窗口的最大值
}
return result;
}
};
优解:可利用map记录该元素的出现次数,再对次数进行排序。时间差异主要出现在排序。要找到前k个高频出现的元素,推荐使用堆排序(又称优先级队列,仅有接口从队头取出元素,队尾插入元素),可以很方便的到结果,
但是对于该题第一时间会想到使用大根堆进行排序,若使用大根堆,当排序元素(出现次数)刚好为k,那么每次都将一个最大值弹出,需要排序弹出k次。并且还需要一个额外的空间去接收弹出的元素.相反使用小根堆,每次把最小的值弹出,最后剩k个元素即为结果,当排序元素仅为k时,就不需要排序直接输出,也不需要额外空间存储结果。
代码:(二刷再总结)