leetocde-滑动数组01

54 阅读7分钟

跟孙哥学java

孙哥主页

滑动窗口的思想

滑动窗口的思想非常简单,如下图所示,假如窗口的大小是3,当不断有新数据来时,我们会维护一个大小 为3的一个区间,超过3的就将新的放入老的移走。 这个过程有点像火车在铁轨上跑,原始数据可能保存在一个很大的空间里(铁轨),但是我们标记的小区间就 像一列长度固定的火车,一直向前走。 有了区间,那我们就可以造题了,例如让你找序列上三个连续数字的最大和是多少,或者子数组平均数是 多少(LeetCode643)等等。 从上面的图可以看到,所谓窗口就是建立两个索引,left和right,并且保持left,right)之间一共有3个元 素,然后一边遍历序列,一边寻找,每改变一次就标记一下当前区间的最大值就行了。 这个例子已经告诉我们了什么是窗口、什么是窗口的滑动:

窗口:窗口其实就是两个变量lft和right之间的元素,也可以理解为一个区间。窗口大小可能固定,也 可能变化,如果是固定大小的,那么自然要先确定窗口是否越界,再执行逻辑处理。如果不是固定的, 就要先判断是否满足要求,再执行逻辑处理。 滑动:说明这个窗口是移动的,事实上移动的仍然是Ift和righti两个变量,而不是序列中的元素。当变 量移动的时,其中间的元素必然会发生变化,因此就有了这种不断滑动的效果。 在实际问题中,窗口大小不一定是固定的,我们可以思考两种场景: 1.固定窗口的滑动就是火车行驶这种大小不变的移动。 2.可变的窗口就像两个老师带着一队学生外出,一个负责开路,一个负责断后,中间则是小朋友。两位老 师之间的距离可能有时大有时小,但是整体窗口是不断滑动的。 根据窗口大小是否固定,可以造出两种类型的题: 1.如果是固定的,则一般会让你求哪个窗口的元素最大、最小、平均值、和最大、和最小等等类型的问 题。 2.如果窗口是变的,则一般会让你求一个序列里最大、最小窗口是什么等等。 滑动窗口题目本身没有太高的思维含量,但是实际在解题的时候仍然会感觉比较吃力,主要原因有以下几 点: 1.解题最终要落实到数组上,特别是边界处理上,这是容易晕的地方,稍有疏忽就难以得到准确的结果。 2.有些元素的比较、判断等比较麻烦,不仅要借助集合等工具,而且处理过程中还有一些技巧,如果不熟 悉会导致解题难度非常大。 3.堆!我们在前面介绍过,堆结构非常适合在流数据中找固定区间内的最大、最小等问题。因此比滑动窗口 经常和堆一起使用可以完美解决很多复杂的问题。 最后一个问题,那双指针和滑动窗口啥区别呢?根据性质我们可以看到,滑动窗口是双指针的一种类型, 主要关注两个指针之间元素的情况,因此范围更小一些,而双指针的应用范围更大,花样也更多。

两个入门题

子数组最大平均数

子数组最大平均数 LeetCode643给定n个整数,找出平均数最大且长度为k的连续子数组,并输出该最大平均数。 image.png

class Solution {
    public double findMaxAverage(int[] nums, int k) {
        int sum=0;
        if(nums.length<k) return -1;
        for (int i = 0; i < k; i++) {
            sum+=nums[i];
        }
        int start=0;
        int max=sum;
        for (int i = k; i < nums.length; i++) {
            sum=sum-nums[start++]+nums[i];
            max=Math.max(max,sum);
        }
        return max*1.0/k;

    }
}

最长连续增长序列

我们再看一个窗户变化的情况。LeetCode674.给定一个未经排序的整数数组,找到最长且连续递增的子序 列,并返回该序列的长度。 连续增长序列 连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

image.png

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if(nums==null||nums.length==0) return 0;
        int max=1;
        int l=0;
        int r=1;
        int len=nums.length;
        while (r<len){
            if(nums[r]-nums[r-1]>0){
                //递增
                r++;
                if(r==len){
                    max=Math.max(max,r-l);
                }
                continue;
            }else {
                //不递增了
                max=Math.max(max,r-l);
                l=r++;
            }

        }
        return max;

    }
}

最长字串专题

先来看一道高频算法题:无重复字符的最长子串。具体要求是给定一个字符串S,请你找出其中不含有重 复字符的最长子串的长度。例如,输入:s="abcabcbb"则输出3,因为无重复字符的最长子串是"abc", 所以其长度为3。 怎么做后面再说,如果再变一下要求,至多包含两个不同字符的最长子串,该怎么做呢? 再变一下要求,至多包含K个不同字符的最长子串,该怎么做呢? 到这里是否感觉,这不在造题吗?是的!上面就分别是LeetCode3、159、340题,而且这几道题都可以用 滑动窗口来解决。学会之后,我们就总结出滑动窗口的解题模板了。 接下来,我们就一道一道看。

无重复字符的最长子串

LeetCode3给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度。例如: 无重复字符的最长子串 image.png

class Solution {
    public static void main(String[] args) {
        String s="abcabcbb";
        int i = lengthOfLongestSubstring(s);
    }
    public static int lengthOfLongestSubstring(String s) {
 
        if(s.length()==0) return 0;
        char[] chars = s.toCharArray();
        int l=0;
        int max=1;
        List<Character> list=new ArrayList<>();

        list.add(chars[l]);
        for (int r = 1; r < chars.length; r++) {
            if(list.contains(chars[r])){
                //重复了
                max=Math.max(max,r-l);
                while (list.contains(chars[r])&&l<r){
                    list.remove(0);
                    l++;
                }

            }
            list.add(chars[r]);
            if(r==chars.length-1){
                max=Math.max(max,r-l+1);
            }
        }
        return max ;

    }
}

至多包含两个不同字符的最长子串

给定一个字符串s,找出至多包含两个不同字符的最长子串t,并返回该子串的长度,这就是 LeetCode159题。例如: image.png

class Solution {
    public static void main(String[] args) {
        String s="eceba";
        int i = lengthOfLongestSubstringTwoDistinct(s);
        System.out.println(i);
    }
    public static int lengthOfLongestSubstringTwoDistinct(String s) {
        Map<Character,Integer> map=new HashMap<>();
        if(s.length()<3){
            return s.length();
        }
        int len=s.length();
        int l=0;
        int r=0;
        int max=0;
        while (r<len){
            char c = s.charAt(r);
            map.put(c,map.getOrDefault(c,0)+1);
            if(map.size()>2){
                //超过两个字符了
                max=Math.max(max,r-l);
                while (map.size()>2){
                    map.put(s.charAt(l),map.getOrDefault(s.charAt(l),0)-1);
                    if(map.get(s.charAt(l))==0){
                        map.remove(s.charAt(l));
                    }
                    l++;
                }
            }
            r++;
            if(r==len){
                max=Math.max(max,r-l);
            }

        }
        return max;


    }
}

至多包含K个不同字符的最长子串

把上面的2改成K既可以

class Solution {
    public static void main(String[] args) {
        String s="eceba";
        int i = lengthOfLongestSubstringTwoDistinct(s,int 2);
        System.out.println(i);
    }
    public static int lengthOfLongestSubstringTwoDistinct(String s,int k) {
        Map<Character,Integer> map=new HashMap<>();
        if(s.length()<3){
            return s.length();
        }
        int len=s.length();
        int l=0;
        int r=0;
        int max=0;
        while (r<len){
            char c = s.charAt(r);
            map.put(c,map.getOrDefault(c,0)+1);
            if(map.size()>k){
                //超过两个字符了
                max=Math.max(max,r-l);
                while (map.size()>2){
                    map.put(s.charAt(l),map.getOrDefault(s.charAt(l),0)-1);
                    if(map.get(s.charAt(l))==0){
                        map.remove(s.charAt(l));
                    }
                    l++;
                }
            }
            r++;
            if(r==len){
                max=Math.max(max,r-l);
            }

        }
        return max;


    }
}

长度最小的子数组

LeetCode209.长度最小的子数组,给定一个含有n个正整数的数组和一个正整数target。 找出该数组中满足其和≥target的长度最小的连续子数组[numsl,numsl+-1,,numsr-1,numsr],并 返回其长度。如果不存在符合条件的子数组,返回0。 长度最小的子数组 image.png

class Solution {
    public static void main(String[] args) {
        int[] nums={1,2,3,4,5};
        System.out.println(minSubArrayLen(11, nums));
    }
    public static int minSubArrayLen(int target, int[] nums) {
        int l=0;
        int min=nums.length+1;
        int r=0;
        int len=min-1;
        int sum=0;
        while (r<len){
            sum+=nums[r++];
            while (sum>=target){
                    min=Math.min(min,r-l);
                sum-=nums[l++];
            }
        }
        return min!=nums.length+1?min:0;

    }
}