栈和队列(8)-- 最大值减去最小值小于或者等于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)的解法。

思考:

普通的解法找到arr所有的子数组O(N²)个,然后对每个子数组求max和min,过程时间复杂度为N²·N,此方法行不通。现在的方法如下:生成两个双端队列qmax和qmin,当子数组为arr[i..j]时,qmax维护了窗口子数组arr[i..j]的最大值更新的结构,qmin维护了窗口子数组arr[i..j]的最小值更新的结构。当子数组arr[i..j]向右扩一个位置变成arr[i..j+1]时,qmax和qmin结构可以在O(1)的时间内更新,并且可以在相同的时间内得到arr[i..j+1]的最大值和最小值。同时可以得出两个结论:

  • 如果子数组arr[i..j]满足条件,那么arr[k..l](i<=k<=l<=j)都满足条件。
  • 如果子数组arr[i..j]不满足条件,那么arr[k..l](k<=i<=j<=l)都不满足条件。
设计过程如下:

1.生成qmax和qmin,同时生成两个整型变量i和j,表示子数组的范围,即arr[i..j],生成整型变量res,表示所有满足天骄的子数组数量。

2.令j不断向右移动(j++),表示arr[i..j]一直向右扩大,不断更新qmax和qmin结构。一旦出现arr[i..j]不满足条件的情况,j向右扩的过程停止,此时arr[i..j-1]、arr[i..j-2]、arr[i..j-3]、……、arr[i..i]都是满足条件的。也就是说满足条件的个数为j - i,即令res = j - i 。

3.完成步骤2,再令i向右移动一个位置,并对qmax和qmin进行更新,此时是arr[i+1..j]窗口的最大值和最小值的更新结构。然后反复重复步骤2。

4.根据步骤2和3,依次求出以arr[0]、arr[1]……、arr[N]作为第一个元素的子数组中满足条件的数量分别有多少个,累积起来的数量就是最终的结果。

整个过程由于小标值最多进qmax和qmin一次和出一次,所以过程的时间复杂度为O(N)。

实现代码:

package algorithm_8;

import java.util.LinkedList;

public class algorithm_8 {
	public static int getNum(int[] arr , int num){
		if (arr == null || arr.length == 0){
			return 0 ;
		}
		LinkedList<Integer> qmin = new LinkedList<Integer>();
		LinkedList<Integer> qmax = new LinkedList<Integer>();
		int i = 0;
		int j = 0;
		int res = 0;
		while(i < arr.length){
			while (j < arr.length){
				while(!qmin.isEmpty() && arr[qmax.peekLast()] >= arr[j] ){
					qmin.pollLast();
				}
				qmin.addLast(j);
				while(!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[j]){
					qmax.pollLast();
				}
				qmax.addLast(j);
				if(arr[qmax.getFirst()] - arr[qmin.getFirst()] > num){
					break;
				}
				j++;
			}
			if(qmin.peekFirst() == i){
				qmin.pollFirst();
				}
			if(qmax.peekFirst() == i){
				qmax.pollFirst();
			}
			res += j - i;
			i++;
		}
		return res;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
     int[] arr_test = {1,2,3,4,5,6,7,8,9};
     int num_test = 4;
     int res_test;
     res_test = getNum(arr_test,num_test);
     System.out.printf("res = %d",res_test);
	}

}
实现结果:

res = 41

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值