JZ59 滑动窗口的最大值
给定一个长度为 n 的数组 nums 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。
例如,如果输入数组{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≤size≤n≤100000,数组中每个元素的值满足 ∣val∣≤100000
要求:空间复杂度 O(n),时间复杂度 O(n)
单调队列
- 可以从队列的两头插入和读取队列元素;
- 队列的元素按照升序或者降序来排列;
解决思路
队列中保存的是当前窗口中的最大值的下标和比最大值的下标还要大的值的下标,将后面这些下标按照值的大小降序排列;
在这个题中,如果a[i]>a[j],并且i>j,则i以前的所有元素都已经没有用了,所有需要将所有小于a[i]值的元素下标都弹出队列,队首保存的永远都是当前窗口的最大值;
每次移动窗口后,先将移入的元素做进队处理,然后再判断当前队首元素的下标是否已经失效,若未失效则直接将队首元素放入结果数组,否则弹出队首元素,再将当前的队首元素放入结果数组。
实现
struct queue{
int arr[100000];
int top;
int tail;
};
void push(struct queue* q,int e){
q->arr[q->top]=e;
q->top++;
}
void pop(struct queue* q){
q->top--;
}
/**
单调队列
*/
void myPush(struct queue* q,int* num,int i){
//进队列需要判断,如果当前元素大于之前所有元素,则将,其他元素都弹出
if(q->tail!=q->top){
//如果标记发生改变则表示当前元素为队列中的最小值,放在队尾
int flag=0;
for(int j=q->tail;j<q->top;j++){
if(num[q->arr[j]]<=num[i]){//找到第一个比当前数小的
//将队列中这个元素之后的所有元素弹出,因为在这个元素前面的元素已经都没用了
while(j!=q->top){
pop(q);
}
push(q, i);
flag=1;
}
}
if(flag==0){
push(q, i);
}
}
else{//如果当前队列为空则直接入队
push(q, i);
}
}
/**
*
* @param num int整型一维数组
* @param numLen int num数组长度
* @param size int整型
* @return int整型一维数组
* @return int* returnSize 返回数组行数
*
* C语言声明定义全局变量请加上static,防止重复定义
*/
int* maxInWindows(int* num, int numLen, int size, int* returnSize ) {
// write code here
struct queue* q=(struct queue*)malloc(sizeof(struct queue));
q->top=0;
q->tail=0;
int* rarr=(int*)malloc(sizeof(int)*100000);
for(int i=0;i<numLen;i++){
//判断当前窗口中的元素个数是否距离触发求最大值的个数-1个
if(i>=size-1){
//窗口元素个数已齐,开始比较最大值
//先将当前元素入队列
myPush(q,num,i);
if(q->arr[q->tail]>i-size){//判断对首元素是否还在窗口内,在则直接放入结果数组
rarr[(*returnSize)++]=num[q->arr[q->tail]];
}else{
//不在则弹出队首元素,将之后的队首元素加入结果数组
q->tail++;
rarr[(*returnSize)++]=num[q->arr[q->tail]];
}
}
else{
myPush(q,num,i);
}
}
return rarr;
}
题目链接:牛客网滑块最大值题目入口