单调队列
单调队列的概念和操作过程
概念:
单调队列和单调栈在操作上有相似之处,但因为单调队列是队列,所以多了一项特殊操作,即头部的元素可以出队,相当于滑动窗口向后滑动。这头部的出队操作就相当于淘汰,用以维护窗口的滑动。
以下通过图片来解释单调队列的操作:
继续入队,直到入队索引2元素时,违反了队列里的单调性:
现在就需要将违反单调性的元素进行从对位剔除,也就是tail指针向前,直到到不违反单调性或者与head指针相同;
然后入队索引2元素:
现在入队索引3,然后又违反了单调性,剔除索引2,入队索引3:
由于上面没有发生淘汰的过程,我就没有说从头部淘汰的过程,现在经过操作到达图中情况:
现在索引在6的位置,头部索引在3,6 - 3 = 3 = k,那么就需要从头部淘汰索引3的元素,那头指针向后移动一个,这就是淘汰的操作:
那么整个的单调队列的操作,就是这样最终队列应该是这样:
单调队列的作用:
单调队列的主要作用是在处理数据序列中,帮助解决需要维护一定单调性的问题,通常是查找滑动窗口内的最大或最小值。其作用包括:
查找滑动窗口内的最大值或最小值:单调队列可以在O(1)的时间复杂度内获取当前滑动窗口的最大值或最小值,而不需要遍历整个窗口,这在实时数据处理中非常有用。
优化数据流处理:当需要实时处理数据流并获取特定窗口内的信息时,单调队列可以提供高效的解决方案,减少不必要的计算和遍历。
优化某些动态规划问题:在一些动态规划问题中,需要维护满足特定条件的一系列元素,单调队列可以帮助高效地实现这种要求。
解决其他需要维护单调性的问题:除了滑动窗口问题,单调队列还可用于解决其他需要维护单调性的问题,如找到数组中连续子数组的最大或最小值。
总之,单调队列的主要作用是通过维护一个队列,使其中的元素保持一定单调性,以提高特定问题的解决效率,尤其在需要实时处理数据流或窗口数据的情况下,它具有显著的性能优势。
例题:
力扣剑指offer59
这个过程就和上面的过程是完全一样的,只是题目是维护单调递减,而我上面的图片是维护的单调递增,思路都是一模一样的;int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize){ int q[numsSize];//创建队列 memset(q, 0, sizeof(q));//初始化队列 int head = 0, tail = 0;//初始化指针 int *ret = (int *)calloc(sizeof(int), numsSize);//创建返回数组 int cnt = 0;//记录返回数组元素个数 for (int i = 0; i < numsSize; i++) { while (head != tail && nums[i] >= nums[q[tail - 1]]) tail--;//违反单调性就剔除 q[tail++] = i;//将当前索引入队 if (i - k >= q[head]) head++;//当头部元素不在窗口时,淘汰 if (i < k - 1) continue; //没有达到窗口大小,继续入队 ret[cnt++] = nums[q[head]];//头部元素就是窗口最大值,维护的是单调递减,所以头部就是最大值,反之就是最小值 } *returnSize = cnt; return ret; }