最大值减去最小值小于或等于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)的解法。
思路
有两个规律
如果max(arr[i..j]) - min(arr[i..j]) <= num,那么ij往里缩,仍然满足要求
因为范围缩小后,最大值只能比当前最大值小,而最小值只能比当前大,那么差值会变小,故仍满组要求
如果max(arr[i..j]) - min(arr[i..j]) > num,不满足要求,那么ij往外扩大,仍不满足要求
因为范围扩大,最大值可能比当前还大,最小值可能比当前值还小,那么差值会变大,仍不满足要求
利用双端队列,利用双端队列动态记录子数组的大小值
qmax和qmin是双端队列,记录大小值的序号,且满足数组的顺序是有序的
由于过程中ij一直只增不减,且到末尾就停止,相当于2n次遍历,
而更新qmax和qmin时,所有的数字都是进出各一次队列,相当2n次遍历
故一共4n次遍历,时间复杂度是o(n)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public static int getNum(int[] arr, int num){ if(arr == null || arr.length == 0){ return 0; } LinkedList<Integer> qmax = new LinkedList<Integer>(); LinkedList<Integer> qmin = new LinkedList<Integer>(); int i = 0; int j = 0; int res = 0; while(i < arr.length){ while(j < arr.length){ //更新qmax,满足依次递减 while(!qmax.isEmpty() && arr[j] >= arr[qmax.getLast()]){ qmax.removeLast(); } qmax.add(j); //更新qmin,满足依次递增 while(!qmin.isEmpty() && arr[j] <= arr[qmin.getLast()]){ qmin.removeLast(); } qmin.add(j); //判断是否不符条件,如果不符,后面的都不会符合 if(arr[qmax.getFirst()] - arr[qmin.getFirst()] <= num){ //这里不能更新res,因为当i增加的时候j一直都是在最后,不会进入到此循环内,不能正确的计数 //res ++; }else{ break; } j++; } //更新窗口,因为i会变化,所以要将原来的pop if(qmax.getFirst() <= i){ qmax.removeFirst(); } if(qmin.getFirst() <= i){ qmin.removeFirst(); } //这里表示[i,i+1],[i,i+2],...,[i,j-1][i,j]都是符合条件的 //一共是j-i组 res += j-i; i++; } return res; }
|