最大值减去最小值小于或等于num的子数组数量
最大值减去最小值小于或等于num的子数组数量
《程序员代码面试指南》第10题 P31 难度:校★★★☆
本题刚开始理解错误,导致想了一个小时没想出来。没想到题目的意思是子数组是连续的(arr[i..j]代表连续的意思。。醉了)
这题也算是生成窗口最大值数组问题的进阶版,核心思路还是用滑动窗口,只不过一个维护窗口子数组的最大值,另一个维护其最小值。
根据2个前提条件:
- 如果子数组arr[i..j]满足条件,则arr[i..j]的每一个子数组也都满足条件
- 如果子数组arr[i..j]不满足条件,则包含arr[i..j]的子数组都不满足条件
可以得到整个过程如下:
- 生成两个双端队列qmax、qmin,两个整型变量i、j,整型变量res(用来表示所有满足的子数组数量)
- j不断++,即窗口右扩,qmax和qmin不断更新(更新规则见生成窗口最大值数组),直到子数组不满足条件,则停止右扩。此时其中以arr[i]为首的所有子数组都满足条件,res+=j-i
- i向右移动一个位置,再次执行步骤2,即求其中以arr[i+1]为首的所有满足条件的子数组的数量
- 步骤2和3循环直到结束,最后求出以arr[0]、arr[1]、arr[2]……为首的所有满足条件的子数组的数量的总和即为答案
牛客题解代码如下:
import java.util.LinkedList;
import java.util.Scanner;
public class Main {
public static int getNum(int[] arr, int num) {
if (arr == null || arr.length == 0) return 0;
LinkedList<Integer> qmin = new LinkedList<>();
LinkedList<Integer> qmax = new LinkedList<>();
int i = 0, j = 0, res = 0;
while (i < arr.length) {
// 求以arr[i]为开头,满足条件最长的子序列
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);
}
// 当不再满足题意,max(arr[i...j] - min(arr[i...j]) <= num 退出当前循环
if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num)
break;
j++;
}
// 以arr[i]为第一个元素的子数组,满足条件的j-i个
res += j - i;
// 下一次从i+1开始,删除队列里不再框内的元素arr[i]
if (qmin.peekFirst() == i) qmin.pollFirst();
if (qmax.peekFirst() == i) qmax.pollFirst();
i++;
}
return res;
}
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int num = sc.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
int res = getNum(arr, num);
System.out.println(res);
}
}

浙公网安备 33010602011771号