第一章 栈和队列(最大值减去最小值小于或等于num的子数组数量)

【题目】

        给定数组arr和整数num,共返回有多少个子数组满足如下情况:

        max{arr[i..j]} - min{arr[i..j]} <= num,子数组中的最大值减去最小值小于或等于num。

【基本思路】

        首先明确两点:

  1、如果子数组arr[i…j]满足条件,那么arr[i…j]中的子数组一定也满足条件。
  2、如果子数组arr[i…j]不满足条件,那么包含arr[i…j]的子数组一定也不满足条件

过程:

        1、使用两个双端队列qmin和qmax,qmin用来维护子数组arr[i…j]的最小值更新,qmax用来维护子数组arr[i…j]的最大值更新。队头表示的就是子数组arr[i…j]的最小(大)值。生成两个整型变量i和j,用于表示子数组的范围,即arr[i…j]。整型变量res表示所有满足条件的子数组数量。初始时,i、j、res都为0。
        2、令j不断向右移动,表示arr[i…j]一直向右扩张,并不断更新qmin和qmax。一旦出现arr[i…j]不满足条件的情况,扩张停止,此时arr[i…j-1]、arr[i…j-2]、arr[i…j-3]…arr[i…i]一定满足条件。
即所有必须以arr[i]开头的满足条件的子数组数量为j - i。所以令 res += j - i。
        3、向右扩张停止后,令i向右移动一个单位,表示开始考虑以arr[i+1]开头的满足条件的子数组数量,更新qmin、qmax。接下来的过程和上述一样。

package base;

import java.util.LinkedList;

public class ChildArr_P31 {

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int num = 2;
        System.out.println(getNum(arr,num));
    }
    static public int getNum(int[] arr, int num) {
        // qmin 放入遍历过程中的值,从小到大排列
        LinkedList<Integer> qmin = new LinkedList<Integer>();
        // qmax 放入遍历过程中的值,从大到下排列
        LinkedList<Integer> qmax = new LinkedList<Integer>();
        int i = 0;
        int j = 0;
        int res = 0;
        while (i < arr.length) {
            while (j < arr.length) {
                // 避免重复插入
                if (qmin.isEmpty() || qmin.peekLast() != j) {
                    while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[j]) {
                        qmin.pollLast();
                    }
                    qmin.push(j);
                    while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]) {
                        qmax.pollLast();
                    }
                    qmax.push(j);
                }
                // 如果最大值减去最小值 大于 num 退出循环
                if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num) {
                    break;
                }
                j++;
            }
            /*
               重要结论:
                    1、如果子数组arr[i..j]满足条件,即max{arr[i..j]} - min{arr[i..j]} < num 那么 arr[i..j] 中的每一个子数组,即
               arr[k..l] 都满足条件 (i<=k<=l<=j)
                    2、此时 arr[i…j-1]、arr[i…j-2]、arr[i…j-3]…arr[i…i]一定满足条件。即所有必须以arr[i]开头的满足条件的子数组数量为j - i。
                    所以令 res += j - i
             */
            res += j - i;
            // i 向右移动后,队首的i需要移除
            if (qmin.getFirst() == i) {
                qmin.removeFirst();
            }
            if (qmax.getFirst() == i) {
                qmax.removeFirst();
            }
            i++;
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值