每日一题 为了工作 2020 0312 第十题

Posted walxt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一题 为了工作 2020 0312 第十题相关的知识,希望对你有一定的参考价值。

/**
*
* 问题:
* 最大值减去最小值小于或等于num子数组的数量
* 给定数组arr和整数num,共返回有多少个子数组满足如下情况:
* 1.max(arr[i..j])-min(arr[i..j]) <= num
* 2.max(arr[i..j])表示子数组arr[i..j]中的最大值
* 3.min(arr[i..j])表示子数组arr[i..j]中的最小值
*
* 要求:
* 如果数组长度为N,请实现时间复杂度为O(N)的解法。
*
* 解答:
* 首先介绍普通的解法,找到arr的所有子数组,一共有O(N^2)个,然后对每一个子数组做遍历找到其中的最小值和最大值,这个过程
*的时间复杂度为O(N),然后看看这个子数组是否满足条件。统计其中所有满足条件的子数组数量即可。普通解法容易实现,但是时间复杂度为
*O(N^3),希望达到O(N)的时间复杂度需要使用双端队列。
*
* 生成两个双端队列qmax和qmin。当子数组为arr[i..j]时,qmax维护了窗口子数组arr[i..j]的最大值更新结构,qmin维护
*了窗口子数组arr[i..j]的最小值更新结构。
*当数子组arr[i..j]向右扩一个位置变成arr[i..j+1]时,qmax和qmin结构的更新代价的平均时间复杂度为O(1),并且可以再O(1)的
*时间内得到arr[i..j+1]的最大值和最小值。
*当子数组arr[i..j]向左扩一个位置变成arr[i+1..j]时,qmax和qmin结构的更新代价的平均时间复杂度为O(1),并且可以再O(1)的
*时间内得到arr[i+1..j]的最大值和最小值。
*
* 通过分析题目满足的条件,可以得到如下两个结论。
*
* 1.如果子数组arr[i..j]满足条件,即为max(arr[i..j])-min(arr[i..j]) <= num,那么arr[i..j]中的每一个子
* 数组,即为arr[k..l](i<=k<=l<=j)都满足条件,我们以子数组arr[i..j-1]为例说明,arr[i..j-1]的最大值只可能小
* 于或等于arr[i..j]的最大值,arr[i..j-1]的最小值只可能大于或等于arr[i..j]的最小值,所以于或等于arr[i..j-1]
* 必然可以满足条件。同理,arr[i..j]中的每一个子数组都满足条件。
* 2.如果子数组arr[i..j]不满足条件,那么所有包含arr[i..j]中的每一个子数组都不满足条件。即为arr[k..l](k<=i<=j<=k)
*
*代码设计:
*1.生成双端队列qmax和qmin。生成两个整形变量i和j,表示子数组的范围,即为arr[i..j]。生成整形变量res,表示所有满足条件的子数组的
*数量。
*2.令j不断向右移动(j++),表示arr[i..j]不断向右扩大,并不断更新qmax和qmin结构,保证qmax和qmin始终维持动态窗口的最大值和最小
*值的更新结构,一旦出现arr[i..j]不满足条件的情况,j向右扩的过程停止此时arr[i..j-1],arr[i..j-2],...arr[i..i+1],
*arr[i..i]一定都是满足条件的,所有以arr[i]作为第一个元素的子数组,满足条件数量为j-1个。于是令res += j-1.
*3.当进行完步骤2,令i向右移动一个位置,并对qmax和qmin做出相应的更新,然后重复步骤2即可。
* ,
*分析:
* 上述代码设计过程中,所有的下标值最多进qmax和qmin一次,出qmax和qmin一次,i和j的值也是不断增加,并且从来没有减少,所以整个
*代码的时间复杂度是O(N)。
*
* @author 雪瞳
*
*/

import java.util.LinkedList;

public class getNum {
    
    public static int getNumArray(int arr[],int num) {
        if(arr == null || arr.length == 0 || num < 0) {
            return 0;
        }
        LinkedList<Integer> qmax = new LinkedList<>();
        LinkedList<Integer> qmin = new LinkedList<>();
        int i = 0;
        int j = 0;
        int res = 0;
        //假设数组上一遍历状态为i..j,则i+1..j是都可以满足条件要求的,下一遍历状态可能达到i+1..k(k>j)
        while(i<arr.length) {
            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;
            //弹出队列首元素
            if(qmin.peekFirst() == i) {
                qmin.pollFirst();
            }
            if(qmax.peekFirst() == i) {
                qmax.pollFirst();
            }
            i++;
        }
    
        return res;
    }

 

以上是关于每日一题 为了工作 2020 0312 第十题的主要内容,如果未能解决你的问题,请参考以下文章

每日一题 为了工作 2020 0322 第二十题

每日一题 为了工作 2020 0501 第六十题

每日一题 为了工作 2020 03019 第十七题

每日一题 为了工作 2020 0317 第十五题

每日一题 为了工作 2020 0315 第十三题

每日一题 为了工作 2020 0502 第六十一题