前言---
最后一篇补完,我就要回宿舍了,在这美好的中秋之夜,吃火锅了,加油,CCSGTC!!!
最大值减去最小值小于或等于num的子数组的数量
题目:
给定数组arr和整数num,返回有多少个子数组满足max(array[i...j]) - min(array[i...j]) <=num.
max(array[i...j])表示array[i...j]中的最大值:
min(array[i...j])表示array[i,.j]中的最小值
要求实现的复杂度为O(N)。
分析:
这道题如果暴力的话,是很简单的,也就是O(n^3),但是复杂度太差了,要是n的数值大一点,就很容易TLE了。
先给出两个结论:
(1)
如果当前所在的数组Array[j...k]满足 max - min <=num; 那么以这个数组的所有子数组Array[l, r]
( j <=l <= r <=k)也都满足max - min <= num;
证明也是很容易理解的:
其任意的子数组的max一定<=其自身的max,任意子数组的min一定>=其自身的min ,所以该结论成立。
(2)
如果一个数组Array[j...k]不满足 max - min <=num;那么任何包含该数组的数组来说,也一定不满足max - min <=num;
证明同第一个结论一样易于理解:
设包含Array[j...k]的数组为Array[l ...r](l <= j <= k <= r),显然,Array[l...r]的max一定>=Array[j...k],
Array[l...r]的min一定<=Array[j...k]。
这两个结论,大家一定要先理解。
这个题目的优秀做法是,通过维护qmax的双端单调递减队列,来求子数组的最大值;通过维护qmin的双端单调递减队列,来
求子数组的最小值。
原理是:通过求以Array[i]( 0<=i<=length-1)为起点的满足条件的子数组数量,每个起点对应的答案加起来就是总数量了。
这边就是要注意一个下标i指针移动,最大值和最小值可能会改变,此时要弹出队列的左端点,跟本博客中“滑动窗口求最值"原理一样,读者不理解可去看看,这边不再细说。
附代码(Java)
package code_180;
import java.util.*;
public class subArrayNum {
LinkedList<Integer>qmax ;//基于LinkedList实现的双端队列。用于维护最大值
LinkedList<Integer>qmin;//用于维护当前数组的最小值
subArrayNum(){
qmax = new LinkedList<Integer>();
qmin = new LinkedList<Integer>();
}
public int getMax( int []array, int num ) {
int l = 0 ,r = 0;
int ans = 0;
while(l < array.length ) {
while( r < array.length) {
while( !qmax.isEmpty()&& array[r] >= array[qmax.peekLast()]) {
qmax.pollLast();
}
qmax.addLast(r);
while( !qmin.isEmpty() && array[r] <= array[qmin.peekLast()]) {
qmin.pollLast();
}
qmin.addLast(r);
if( array[qmax.peekFirst()] - array[qmin.peekFirst()] > num )
break;
r++;
}
ans += r-l;
if(qmax.peekFirst() == l)
qmax.pollFirst();
if(qmin.peekFirst() == l )
qmin.pollFirst();
l++;
//System.out.println(ans);
//System.out.println("Ok");
}
return ans;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int array[] = {3,2,5,1,4,7,8,6};
subArrayNum myQueue = new subArrayNum();
System.out.println( myQueue.getMax(array, 4));
}
}