• 每日一题 为了工作 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;
        }
  • 相关阅读:
    Java 多线程(一) 基础知识与概念
    hashMap和treeMap
    转:Java IO流学习总结
    hibernate缓存
    java aio nio bio
    java1.8新特性
    LeetCode Contiguous Array
    LeetCode Sort Characters By Frequency
    LeetCode Subarray Sum Equals K
    LeetCode Group Anagrams
  • 原文地址:https://www.cnblogs.com/walxt/p/12468754.html
Copyright © 2020-2023  润新知