算法基础13-《进阶》单调栈和窗口及其更新结构

1 单调栈和窗口及其更新结构

1.1 窗口

1.1.1 滑动窗口是什么?

窗口只是我们脑海中的一个范围,用过L和R来规定我们窗口的边界。保证L<=R这个条件

1、滑动窗口是一种想象出来的数据结构;

2、滑动窗口有左边界L和右边界R

3、在数组或者字符串或者一个序列上,记为S,窗口就是S[L…R]这一部分

4、L往右滑动意味着一个样本出了窗口,R往右滑意味着一个样本进了窗口

5、 L和R都只能往右滑动

1.1.2 滑动窗口能做什么?

滑动窗口、首尾指针等技巧,说白了就是一种求解问题的流程设计。

1.1.3 维护窗口滑动的更新结构

例如我们求窗口内最大值问题

用单调双端队列来实现,双端队列就是我们的双向链表结构。我们保证我们的双端队列从头部到尾部数值是从大到小

1、 如果窗口的R位置往右移动,我们把进入窗口的这个数从尾部加入到双端队列。如果当前数比该数的前一个数大(从尾部看)那么队列中小于等于的数弹出。直到小于队列的前一个数,加入该数。

2、如果窗口的L位置往右移动,预示着有数要出我们的窗口,我们从双端队列的头部观看要出去的数是不是我们头部队列的数。是就弹出头部的数,不是就不做任何操作

3、我们窗口结构一直被维护,双端队列右边进,左边出。那么窗口的最大值就是我们双端队列最左侧(头部)的值

双端队列结构实质上指的是:如果此时形成的窗口状况,不想让R往右动了,而让L往右动。谁会以此成为最大值的优先级。为什么弹出的数不再找回,原因是在窗口滑动的过程中,被弹出的数的优先级已经被后来的大数取代了,这就是尾端加入,前一个数比当前数小则弹出,比当前数大就加入当前数的道理

反之,如果我们要窗口内最小值,只需要维护我们的双端队列单调递增的,既由小到大的即可

复杂度:窗口滑动经过的数,最多进双端队列一次,最多出双端队列一次,如果窗口滑动了N个数,时间复杂度就是O(N),单次平均O(1)。

1.1.4 高频题:求滑动窗口最大值

假设一个固定大小为W的窗口,以此划过arr,返回每一次划出状况的最大值

例如,arr=[4, 3, 5, 4, 3, 3, 6, 7]

返回:[5, 5, 5, 4, 6, 7]

分析:窗口起始是4,3,5,窗口内最大值是5。窗口向右滑动变为3,5,4最大值5…


package class01;

import java.util.LinkedList;

public class Code01_SlidingWindowMaxArray {

	public static int[] getMaxWindow(int[] arr, int w) {
		if (arr == null || w < 1 || arr.length < w) {
			return null;
		}
		
		// Java中LinkedList就是双端队列,双向链表
		// 其中放的是下标位置,头代表 (大->小)尾
		LinkedList<Integer> qmax = new LinkedList<Integer>();
		// 窗口在滑动的过程中,最终会生成arr长度-窗口起始宽度+1个值
		int[] res = new int[arr.length - w + 1];
		int index = 0;
		// L...R
		//     R
		for (int R = 0; R < arr.length; R++) { // 当前让 i -> [i] 进窗口 , i 就是 r
			// R 位置的值  可以放在比他大的数后,或者空
			// 双端队列不为空,且双端队列尾部的值小于当前要进入窗口的值
			while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) {
			    // 双端队列从尾部弹出
				qmax.pollLast();
			}
			// 经过上述的while,最终把当前进入窗口的数放入双端队列的尾部
			qmax.addLast(R);
			// 数进来了
			// 如果窗口没有形成W的长度之前,不弹出数字的
			// 当前下标是R, R-W就是需要过期的下标。
			// 如果双端队列的头部保存的下标等于R-W,就头部弹出。实质R-W就是我们原始结构的L下标
			if (qmax.peekFirst() == R - w) {
				qmax.pollFirst();
			}
			// 以上窗口更新做完了
			// 窗口没有形成W长度之前,不收集答案。形成W长度后,每一次收集一个答案
			if (R >= w - 1) {
				res[index++] = arr[qmax.peekFirst()];
			}
		}
		return res;
	}

	// for test
	public static int[] rightWay(int[] arr, int w) {
		if (arr == null || w < 1 || arr.length < w) {
			return null;
		}
		int[] res = new int[arr.length - w + 1];
		int index = 0;
		int L = 0;
		int R = w - 1;
		while (R < arr.length) {
			int max = arr[L];
			for (int i = L + 1; i <= R; i++) {
				max = Math.max(max, arr[i]);

			}
			res[index++
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值