最大值和最小值之差达标的子数组数量
创始人
2024-04-07 22:20:57
0

1、题目

给定一个整型数组 arr,和一个整数 num

某个 arr 中的子数组 sub,如果想达标,必须满足:sub中最大值 - sub中最小值 <= num。

请返回 arr 中达标子数组的数量。

2、思路

结论1:如果 [L...R] 范围上的 max - min <= sum,则 L...R 范围上的所有子数组都是达标的。

解释:假设 [L'...R'][L...R] 的子数组
[L...R]max - [L...R]min <= sum
[L'...R']max' <= [L...R]max[L'...R']min >= [L...R]min
因此:[L'...R']max' - [L'...R']min <= sum,依然是达标的。

结论2:如果 [L...R] 范围上的 max - min > sum,那么无论 L 往左扩还是 R 往右扩,产生的新的子数组一定是不达标的。

思路:准备一个最大值更新结构qmax 和一个最小值更新结构 qmin,用于维护最大值和最小值。

假设窗口一开始为0,此时 L = 0,让 R 往右扩,每扩一次就更新 qmaxqmin,如果达标就继续扩,一直到位置 ppp 不达标了,那么必须以 0 位置开始的子数组达标的数量一共有 p−0+1=p+1p - 0 + 1 = p + 1p−0+1=p+1 个。

然后 L 往右滑动到1,即 L = 1 时,R 继续往右扩,看是否达标,直到到达不能达标的位置,则能得到必须以 1 位置开始的子数组的达标数量。

注意:该过程中 LR 都没有回退,一共只会滑过 n 个数,所以时间复杂度是 O(n)O(n)O(n)。

  • C++ 版
/*************************************************************************> File Name: 002.最大值和最小值差达标的子数组数量.cpp> Author: Maureen > Created Time: 四 11/10 18:06:16 2022************************************************************************/#include 
#include 
#include 
using namespace std;//暴力对数器 O(n^3)
int right(vector &arr, int sum) {if (arr.size() == 0 || sum < 0) {return 0;}int n = arr.size();int count = 0;for (int l = 0; l < n; l++) {for (int r = l; r < n; r++) {int maxV = arr[l];int minV = arr[l];for (int i = l + 1; i <= r; i++) {maxV = max(maxV, arr[i]);minV = min(minV, arr[i]);}if (maxV - minV <= sum)count++;}}return count;
}//滑动窗口 O(n)
int way(vector &arr, int sum) {if (arr.size() == 0 || sum < 0) {return 0;}deque qmax; //最大值的更新结构deque qmin; //最小值的更新结构int n = arr.size();int count = 0;int r = 0;for (int l = 0; l < n; l++) { //枚举子数组的开始位置lwhile (r < n) {//最大值的更新while (!qmax.empty() && arr[qmax.back()] <= arr[r]) {qmax.pop_back();}qmax.push_back(r);//最小值的更新while (!qmin.empty() && arr[qmin.back()] >= arr[r]) {qmin.pop_back();}qmin.push_back(r);//该子数组是否达标if (arr[qmax.front()] - arr[qmin.front()] > sum) { //不达标break;} else {r++;}}count += r - l;//记录子数组达标的数量//检查队首元素是否过期if (qmax.front() == l) {qmax.pop_front();}if (qmin.front() == l) {qmin.pop_front();}}return count;
}vector generateRandomArray(int maxL, int maxV) {int len = rand() % (maxL + 1);vector arr(len);for (int i = 0; i < len; i++) {arr[i] = (rand() % (maxV + 1)) - (rand() % (maxV + 1));}return arr;
}void printArray(vector &arr) {if (arr.size()) {for (int i = 0; i < arr.size(); i++) {cout << arr[i] << " ";}cout << endl;}
} int main() {int maxL = 100;int maxV = 200;int testTime = 100000;cout << "Begin to test: " << endl;for (int i = 0; i < testTime; i++) {vector arr = generateRandomArray(maxL, maxV);int sum = rand() % (maxV + 1);int ans1 = right(arr, sum);int ans2 = way(arr, sum);if (ans1 != ans2) {cout << "Oops!" << endl;printArray(arr);cout << sum << endl;cout << ans1 << endl;cout << ans2 << endl;break;}if (i % 100 == 0) cout << i << " cases passed!" << endl;}cout << "Test end!" << endl;return 0;
}
  • Java 版
public class AllLessNumSubArray {// 暴力的对数器方法 O(N^3),枚举所有子数组public static int right(int[] arr, int sum) {if (arr == null || arr.length == 0 || sum < 0) {return 0;}int N = arr.length;int count = 0;for (int L = 0; L < N; L++) {for (int R = L; R < N; R++) {int max = arr[L];int min = arr[L];for (int i = L + 1; i <= R; i++) {max = Math.max(max, arr[i]);min = Math.min(min, arr[i]);}if (max - min <= sum) {count++;}}}return count;}//优化:O(N^3) -> O(N)// 结论1:如果[L...R] 范围上的max - min <= sum,那么 L...R 范围的所有子数组都是达标的//	原因:假设 L'...R'是L...R的子数组,因为[L...R]max - [L...R]min <= sum,那么[L'...R']的max'<= max,而[L'...R']的min' >= min,所以max'- min' <= sum,也是达标的//结论2:如果[L...R] 范围上的max - min > sum,那么无论L往左扩还是R往右扩,产生的新的子数组一定是不达标的//思路:个准备一个最大值和最小值的更新结构qmax和qmin,用于维护最大值和最小值//假设窗口一开始为0,此时L = 0,让R往右扩,每扩一次就更新qmax和qmin,如果达标就继续扩,一直到p位置不达标了,那么必须以0开始的子数组达标的数量一共有p-0+1 = p+1个;//然后L往右滑动到1,即L=1的时候,R继续往右扩,看是否达标,直到到达不能达标的位置,则能得到必须以1开始的子数组的达标数量;//......public static int num(int[] arr, int sum) {if (arr == null || arr.length == 0 || sum < 0) {return 0;}int N = arr.length;int count = 0;LinkedList maxWindow = new LinkedList<>();LinkedList minWindow = new LinkedList<>();int R = 0; //不回退//L和R位置都不回退,一共只过N个数,所以时间复杂度是O(n)for (int L = 0; L < N; L++) {  //依次尝试窗口以0开始[0...、[1...、[2...的情况//[L,R),如果L=R,说明窗口内没数while (R < N) { //[L...R,R往右扩,直到初次不达标停止while (!maxWindow.isEmpty() && arr[maxWindow.peekLast()] <= arr[R]) {maxWindow.pollLast();}maxWindow.addLast(R);while (!minWindow.isEmpty() && arr[minWindow.peekLast()] >= arr[R]) {minWindow.pollLast();}minWindow.addLast(R);if (arr[maxWindow.peekFirst()] - arr[minWindow.peekFirst()] > sum) {break;} else {R++;}}count += R - L;//如果L过期,则滚蛋if (maxWindow.peekFirst() == L) {maxWindow.pollFirst();}if (minWindow.peekFirst() == L) {minWindow.pollFirst();}}return count;}// for testpublic static int[] generateRandomArray(int maxLen, int maxValue) {int len = (int) (Math.random() * (maxLen + 1));int[] arr = new int[len];for (int i = 0; i < len; i++) {arr[i] = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1));}return arr;}// for testpublic static void printArray(int[] arr) {if (arr != null) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}}public static void main(String[] args) {int maxLen = 100;int maxValue = 200;int testTime = 100000;System.out.println("测试开始");for (int i = 0; i < testTime; i++) {int[] arr = generateRandomArray(maxLen, maxValue);int sum = (int) (Math.random() * (maxValue + 1));int ans1 = right(arr, sum);int ans2 = num(arr, sum);if (ans1 != ans2) {System.out.println("Oops!");printArray(arr);System.out.println(sum);System.out.println(ans1);System.out.println(ans2);break;}}System.out.println("测试结束");}
}

相关内容

热门资讯

望海楼教堂导游词 望海楼教堂导游词  著名的望海楼留下太多的故事,也记载着太多名垂千史的成功人物,就让我们跟着导游一起...
大雁塔景区导游词 大雁塔景区导游词  导语:大雁塔位于唐长安城晋昌坊(今陕西省西安市南)的大慈恩寺内,又名“慈恩寺塔”...
阳春石林的导游词 阳春石林的导游词  导游词是导游人员引导游客观光游览时的讲解词,是导游员同游客交流思想,向游客传播文...
青岛栈桥的景点导游词 青岛栈桥的景点导游词  青岛是中国环境最好的城市之一。今天小编为大家整理了青岛的经典导游词,希望对您...
九寨沟旅游导游词 九寨沟旅游导游词(精选30篇)  作为一位兢兢业业的旅游从业人员,就有可能用到导游词,导游词是我们引...
泰州乔园导游词 泰州乔园导游词  泰州乔园是江苏省文物保护单位及省级重点风景名胜区,素称淮水以东第一园。以下是小编精...
西安大雁塔的导游词作文 关于西安大雁塔的导游词作文  一篇完整的导游词,其结构一般包括习惯用语、概括介绍、重点讲解三个部分,...
庐山探美之旅导游词 庐山探美之旅导游词  各位游客,大家好!我是探美旅行社的'导游潘昶皓。欢迎你们来庐山旅游。  庐山位...
沙面小学介绍导游词 沙面小学介绍导游词  不到沙面非尖子,不游珠江真遗憾。  各位旅客们早上好,我叫刘玮,大家可以叫我刘...
介绍成都景点的导游词 介绍成都景点的导游词(精选6篇)  作为一名专门引导游客、助人为乐的导游,时常需要编写导游词,导游词...
陈列馆导游词 陈列馆导游词  作为一名导游,常常需要准备导游词,导游词不是以一代百、千篇一律的,它必须是从实际出发...
浙江大明山景点导游词 浙江大明山景点导游词  作为一名专门为游客提供优质服务的导游人员,编写导游词是必不可少的,导游词不是...
吉林导游词 吉林导游词 15篇  作为一位尽职的导游,就不得不需要编写导游词,导游词是导游员在游览时为口头表达而...
江西鄱阳湖导游词 江西鄱阳湖导游词  作为一名可信赖的导游人员,时常要开展导游词准备工作,导游词具有注重口语化、精简凝...
龙宫风景区导游词 龙宫风景区导游词  导游词是对一个地方的介绍和说明,通过导游词,能让游客更加清晰的了解和明白当地的文...
贝子庙导游词 贝子庙导游词  朋友们,在塞外名城锡林浩特市额尔敦敖包山下,有一座绿野古刹——贝子庙。贝子庙始建于清...
兰州五泉山导游词 兰州五泉山导游词  作为一名导游,通常会被要求编写导游词,导游词作为一种解说的文体,它的作用是帮助游...
湖北恩施大峡谷导游词 湖北恩施大峡谷导游词  大峡谷位于恩施沐抚境内,听人们说,那是很久以前,一次自然灾害形成的奇观。5月...
山西概况的导游词 山西概况的导游词  山西省,简称晋,位处华北,东靠太行山,因在太行山以西,故称山西。省会太原,古时又...
导游词 我做一次小导游 导游词500字 我做一次小导游  作为一名具备丰富知识的导游,就难以避免地要准备导游词,借助导游词可...