左神算法课--窗口和单调栈【算法】

窗口

题目:求数组arry上的子数组个数,子数组从L到R上满足max-min<=num,arry和num为参数。
推论:1、一个子数组满足条件,则子数组内的任意子数组都满足条件。
max-min<=num
max2<=max
min2>=min
则max2-min2<=num
2、一个子数组不达标,向外扩,也不会达标。证明同理
思路:做两个双端队列,max和min,以0位置开头,向右扩展窗口,不达标时左端索引向右移动。
从0到X位置达标,则从0开始的子数组达标数为X-0+1;
从1到Y位置达标,则从1开始的子数组达标数为Y-1+1;
。。。。

 public Integer getNum(int[] array, int num) {
        int res = 0;
        //两个双端队列,用于求最大值和最小值
        LinkedList<Integer> max = new LinkedList<>();
        LinkedList<Integer> min = new LinkedList<>();
        //下标指针
        int R = 0;
        int L = 0;
        while (L < array.length) {
            while (R < array.length) {
//                双端队列操作
                while (!min.isEmpty() && array[min.peekFirst()] >= array[R]) {
                    min.pollLast();
                }
                min.addLast(R);
                while (!max.isEmpty() && array[max.peekFirst()] <= array[R]) {
                    max.pollLast();
                }
                max.addLast(R);
//                如果不达标,退出循环
                if (array[max.getFirst()] - array[min.getFirst()] > num) {
                    break;
                }
                R++;
            }
            //R不达标时,求出个数
            res += R - L;
//            查看数据是否过期,因为左指针要向右移动了。
            if (min.peekFirst() == L) {
            min.pollFirst();
            }
            if (max.peekLast() == L) {
            max.pollFirst();
            }
            L++;
        }
        return res;
    }

单调栈

压入,要求压入的数A要大于下面的数B(或小于),不满足就弹出B

解决问题,求一个数组中每个数的左边和右边比你小的且离你最近的数(无重复数)。

思路:当要压入A时,不满足条件,弹出一个数B,这个数的左边比他小的数就是他底下那个数C,右边比他小的数就是A,记录,继续比较,弹出、压入…如果没有数压入了,弹出时右边小数就没有,底下没数了,则左边小的数也没有。
复杂度O(n);

问题,求一个数组中每个数的左边和右边比你小的且离你最近的数(有重复数)。

    public static int[][] getNearLess(int[] arr) {
        int[][] res = new int[arr.length][2];
        Stack<List<Integer>> stack = new Stack<>();
        for (int i = 0; i < arr.length; i++) {
            while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {
//               弹出整个list
                List<Integer> pop = stack.pop();
                //左边比他小的数的位置
                int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
                for (Integer popi :
                        pop) {
                    res[popi][0] = leftLessIndex;
                    res[popi][1] = i;
                }
            }

            if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) {
                stack.peek().add(Integer.valueOf(i));
            } else {
                ArrayList<Integer> list = new ArrayList<>();
                list.add(i);
                stack.push(list);
            }

        }
        while (!stack.isEmpty()) {
            List<Integer> pop = stack.pop();
            int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
            for (Integer popi :
                    pop) {
                res[popi][0] = leftLessIndex;
                res[popi][1] = -1;
            }

        }
        return res;
    }

题:求数组的子数组L到R上的累加和
前缀和数组:
{1,3,6}
生成一个数组:数组中的值为0到这个位置下标的和。num:{1,4,10}
则L到R的累加和为num[R]-num[L-1}

题:在子数组中有X=(L到R的累加和)*(L到R中的最小值),求X的最大值。
推论:最小值不变时,子数组范围越大,X越大。
{3,4,6,2,8,6}
思路:找到以数组0位置为最小值的范围最大数组{3,4,6}——得到一个X1;
继续以数组1位置为最小值的范围最大数组{4,6}——得到一个X2;

算法:单调栈,求出两边比自己小的数的位置

理解单调性,以单调性为思路去解题;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值