最大值减去最小值小于或等于num的子数组数量

题目

给定数组arr和整数num,保证max(arr[i..j]) - min(arr[i..j]) <=num,其中max(arr[i..j])表示子数组 arr[i..j]中的最大值,min(arr[i..j])表示子数组arr[i..j]中的最小值。假定数组长为N,实现时间复杂度O(N)的解法。

分析

有两个重要逻辑:

1、如果子数组arr[i,j]满足条件,则arr[i,j]中的每一个子数组都满足条件

2、如果子数组arr[i,j]不满足条件,则所有包含arr[i,j]的子数组都不满足条件

这道题目本质上需要计算每个子数组的最大值与最小值的差,判断是否符合要求。所以需要设计一个快速获得最大值和最小值的方法。由于子数组中的数字下标是连续的,可以考虑队列和栈之类的结构来实现。参考“生成窗口最大值数组”,可以用双向队列来实现快速获取最大值和最小值,时间复杂度为O(1)。

于是整个算法的流程为:

(1)生成两个双端队列max和min,利用res记录满足条件的子数组的数量;

(2)令j不断右移,不断更新max和min来判断当前子数组是否满足条件。一旦出现arr[i,j]不满足的情况出现,则j停止向右扩的过程,此时arr[i,j-1],arr[i,j-2],……,arr[i,i]一定均满足条件。于是令res=j-i;

(3)令i右移一个位置,对max和min进行更新,max和min从原来的arr[i,j]窗口更新成arr[i+1,j]窗口的最大值和最小值结构。重复步骤2

代码

public class GetNum {
    public int getNum(int[] arr, int num) {
        if(arr == null || arr.length == 0) {
            return 0;
        }
        LinkedList max = new LinkedList<>();
        LinkedList min = new LinkedList<>();
        int i = 0;
        int j = 0;
        int res = 0;
        while (i < arr.length) {
            while (j < arr.length) {
                while (!max.isEmpty() && arr[j] >= arr[max.peekLast()]) {
                    max.pollLast();
                }
                max.offerLast(j);
                while (!min.isEmpty() && arr[j] <= arr[min.peekLast()]) {
                    min.pollLast();
                }
                min.offerLast(j);
                if(arr[max.peekFirst()] - arr[min.peekFirst()] > num) {
                    break;
                }
                j++;
            }
            if(max.peekFirst() == i) {
                max.pollFirst();
            }
            if(min.peekFirst() == i) {
                min.pollFirst();
            }
            System.out.println(i+" " + j);
            res += j - i;
            i++;
        }
        return res;
    }
}

你可能感兴趣的:(最大值减去最小值小于或等于num的子数组数量)