栈与队列9——最大值减去最小值小于或等于num的子数组的数量

题目

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

max(arr[i..j])-min(arr[i..j])<=num

max表示子数组arr[i..j]中的最大值,min表示最小值


要求

如果数组长度为N,请实现时间复杂度为O(N)的解法


思路

普通解法:双重for循环寻找所有的子数组,在循环内部,对每一个子数组遍历找到其中的最小值和最大值,时间复杂度为O(N),总体的时间复杂度为O(N^3).

最优解: 时间复杂度O(N),额外空间复杂度O(N)

数据结构:双端队列qmax和qmin,可参考之前的“生成窗口最大值数组”文章。

最优解步骤:

明确两个结论:

  • 如果子数组arr[i..j]满足要求,即max(arr[i..j])-min(arr[i..j])<=num,则子数组arr[i..j]中的每一个子数组都满足条件
  • 如果子数组arr[i..j]不满足要求,则所有包含子数组的子数组,都不满足条件
  1. 生成两个双端队列qmax,qmin,两个整形变量i,j表示数组范围即arr[i..j]。整形变量res,表示所有满足条件的子数组数量。
  2. j不断右移,并更新qmin,qmax,一旦arr[i..j]不满足条件,j向右扩张停止,此时,arr[i..j-1]、arr[i..j-2]... arr[i,i]是满足条件的,所以满足条件的子数组的数量为j-i个,令res+=j-i
  3. 完成2之后,i向右移动一个位置,同时更新qmin和qmax,并重复步骤2;
  4. 步骤3和步骤2,完成后,返回res即可

源码

public static int getNum(int[] arr,int num){
	if(arr==null||arr.length==0||num<0){
		return 0;
	}
	//双端队列,qmin存储从小到大,qmax存储从大到小
	LinkedList<Integer> qmin=new LinkedList<Integer>();
	LinkedList<Integer> qmax=new LinkedList<Integer>();
	//arr[i..j]
	int i=0;
	int j=0;
	//res是返回的子数组数量
	int res=0;
	while(i<arr.length){
		//步骤2,右移j
		while(j<arr.length){
                        //初次进入和之后的进入
			if(qmin.isEmpty()||qmin.peekLast()!=j){
				while(!qmin.isEmpty()&&arr[qmin.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++;
		}
                //累加满足条件的子数组数量
		res+=j-i;
		//更新qmin和qmax当i右移时,如果是第一个元素,则需要弹出,否则没有影响
		if(qmin.peekFirst()==i){
			qmin.pollFirst();
		}
		if(qmax.peekFirst()==i){
			qmax.pollFirst();
		}
		i++;
	}
	return res;

}

有问题,欢迎讨论

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值