算法: 整形数组中子数组最大值减最小值达标的子数组个数

题目描述

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

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

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

class Solution {
public:
   int AllLessNumSubArray(vector<int>& arr, int sum) {

    }
};

题目解析

暴力

先找出arr的所有子数组,一共 O ( N 2 ) O(N^2) O(N2)个,然后对每一个子数组遍历找出最大值和最小值,其时间复杂度为 O ( N ) O(N) O(N),然后看看这个子数组是否满足条件

class Solution {
public:
    int AllLessNumSubArray(vector<int>& arr, int sum) {
        int N = arr.size();
        int count = 0;
        for (int L = 0; L < N; ++L) {
            for (int R = L; R < N; ++R) {
                int max = arr[L], min = arr[L];
                for (int i = L + 1; i <= R; ++i) {
                    max = std::max(max, arr[i]);
                    min = std::max(min, arr[i]);
                }
                if(max - min <= sum){
                    ++count;
                }
            }
        }
        return count;
    }
};

单调队列

维护两个双端队列qmax和qmin。当子数组为 a r r [ i . . . j ] arr[i...j] arr[i...j]时,

  • q m a x qmax qmax维护了窗口子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]的最大值更新的结构
  • q m i n qmin qmin维护了窗口子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]的最小值更新的结构

当子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]向右扩一个位置变成 a r r [ i . . . j + 1 ] arr[i...j+1] arr[i...j+1]使, q m a x qmax qmax q m i n qmin qmin结构可以在 O ( 1 ) O(1) O(1)的时间内更新,并且可以在 O ( 1 ) O(1) O(1)的时间内得到 a r r [ i . . . j + 1 ] arr[i...j+1] arr[i...j+1]的最大值和最小值。

当子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]向左缩一个位置变成 a r r [ i + 1... j ] arr[i+1...j] arr[i+1...j]使, q m a x qmax qmax q m i n qmin qmin结构可以在 O ( 1 ) O(1) O(1)的时间内更新,并且可以在 O ( 1 ) O(1) O(1)的时间内得到 a r r [ i . . . j + 1 ] arr[i...j+1] arr[i...j+1]的最大值和最小值。

因此:

  • 如果子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]满足条件,即 m a x ( a r r [ i . . . j ] ) − m i n ( a r r [ i . . . j ] ) < = n u m max(arr[i...j])-min(arr[i...j]) <= num max(arr[i...j])min(arr[i...j])<=num,那么 a r r [ i . . . j ] arr[i...j] arr[i...j]中的每一个子数组,都满足条件
  • 如果子数组 a r r [ i . . . j ] arr[i...j] arr[i...j]不满足条件,即 m a x ( a r r [ i . . . j ] ) − m i n ( a r r [ i . . . j ] ) > n u m max(arr[i...j])-min(arr[i...j]) > num max(arr[i...j])min(arr[i...j])>num,那么 a r r [ i . . . j ] arr[i...j] arr[i...j]中的每一个子数组,都不满足条件

从而,设计方案如下:

  1. 维护两个双端队列qmax和qmin;维护两个整型变量i和j,表示子数组的范围,即 a r r [ i . . . j ] arr[i...j] arr[i...j]
  2. 令j不断往右移动(j++),并不断更新 q m a x qmax qmax q m i n qmin qmin,保持qmax和qmin时钟维持动态窗口最大值和最小值的更新结构
  3. 一旦出现 a r r [ i . . . j ] arr[i...j] arr[i...j]不满足条件的情况, j j j向右扩停止,此时 a r r [ i . . . j − 1 ] 、 a r r [ i . . . j − 2 ] . . . . a r r [ i . . . i ] arr[i...j-1]、arr[i...j-2]....arr[i...i] arr[i...j1]arr[i...j2]....arr[i...i]一定都是满足条件的。也就是说,所有必须以 a r r [ i ] arr[i] arr[i]作为第一个元素的子数组,满足条件的数量为 j − i j-i ji个,于是 a n s + = j − 1 ans += j -1 ans+=j1
  4. 当完成上面步骤,令 i i i向右移动一个位置,并更新 q m a x qmax qmax q m i n qmin qmin,然后重复步骤 2 、 3 2、3 23。也就是求所有必须以 a r r [ i + 1 ] arr[i + 1] arr[i+1]作为第一个元素的子数组中,满足条件的数量
  5. 更新上面步骤,依次求出以 a r r [ 0 ] 、 a r r [ 1 ] . . . . a r r [ N − 1 ] arr[0]、arr[1]....arr[N - 1] arr[0]arr[1]....arr[N1]作为第一个元素的子数组中满足条件的数量分别有多少个,累积起来就是最终的结构

上面过程中,所有下标值最多进qmax和qmin一次,出qmax和qmin一次。i、j只会往右增,从不会减小,因此其时间复杂度为O(N)

class Solution {
public:
    int AllLessNumSubArray(vector<int>& arr, int sum) {
        int N = arr.size();
        int count = 0;
        std::deque<int> qmax , qmin ;
        int R = 0;
        for (int L = 0; L < N; ++L) {
            while (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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值