介绍
求出一个数组中最大值减去最小值小于等于某个数的子数组的个数O(N)时间复杂度
思想
使用两个双端队列结构,一个用于维护当前子数组中的最大值,一个用于维护当前子数组中的最小值
双端队列的维护详见双端队列
首先我们需要知道一下结论:
如果一个子数组左端标记为L,右端标记为R,那么:
(1)如果从L到R构成的子数组可以满足条件,则该子数组中的任意一个子数组都满足条件;
(2)如果从L到R构成的子数组不满足条件,则包含改数组的任何一个数组都不满足条件。
我们一次遍历数组的每一个位置,当前位置记为L,R记为由当前位置可以构成的满足条件的最长子数组,则当由当前位置形成的满足条件的子数组个数为R-L;当无法以L形成满足条件的子数组时L++,并维护一下最大值和最小值的双端队列。
完整代码
#include <iostream>
#include <deque>
using namespace std;
//最大值减去最小值小于或等于num的子数组
int getNum(int arr[], int size, int num){
deque<int> minQ;
deque<int> maxQ;
int L = 0;
int R = 0;
int res = 0;
while(L < size){
while(R < size){
while(!minQ.empty() && arr[minQ.back()] >= arr[R]){
minQ.pop_back();
}
minQ.push_back(R);
while(!maxQ.empty() && arr[maxQ.back()] <= arr[R]){
maxQ.pop_back();
}
maxQ.push_back(R);
if(arr[maxQ.front()]-arr[minQ.front()] > num){
break;
}
R++;
}
res += R-L;
if(maxQ.front() == L){
maxQ.pop_front();
}
if(minQ.front() == L){
minQ.pop_front();
}
L++;
}
return res;
}
int main()
{
int n, num;
cin >>n >> num;
int *arr = new int[n];
for(int i = 0; i < n; i++) cin >> arr[i];
cout << getNum(arr, n, num) << endl;
return 0;
}