leetcode连续数组系列

一、前缀和

560:和为k的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数>
组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

这道题暴力的解法就是先保存数组的前缀和,然后遍历差分,求所有和为K的子数组,时间复杂度为O(n^2)。

一次遍历的解法:遍历求前缀和,利用HashMap将所有前缀和的次数保存起来,遍历到某个前缀和sum时,判断map中是否存在sum-k,如果有,说明有连续数组的和为k。最后将此前缀和次数+1存入map中。此解法时间复杂度为o(n)。注意当前缀和sum==k时,sum-k=0,所以需要初始化map.put(0,1)。

class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        int sum = 0;
        int res = 0;
        for(int i = 0;i < nums.length;++i){
            sum+=nums[i];
            if(map.containsKey(sum-k))
                res+=map.get(sum-k);
            map.put(sum,map.getOrDefault(sum,0)+1);
        }
        return res;
    }
}

525. 连续数组

给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度)。

示例 1:
输入: [0,1,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。

这道题和上面那道题异曲同工,不过需要转换一下思路,同样用前缀和sum(i),不过遇到0加1,遇到1减1,若区间[i,j]的0,1数量相同,那么sum(i-1)sum(j)前缀和相同,那么长度就为j-(i-1),因此我们需要map来保存的是前缀和所在的索引,此外,因为题目求得是最长连续子数组,所以我们不能更新前缀和所在的索引,只保留第一次的索引。注意这道题map初始化应该为map.put(0,-1)

    public int findMaxLength(int[] nums) {
        int res = 0;
        int sum = 0;
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,-1);
        for(int i = 0;i < nums.length;++i){
            sum+= nums[i]==0?1:-1;
            if(map.containsKey(sum)){
                res = Math.max(res,i-map.get(sum));
            }
            else{
                map.put(sum,i);
            }
        }
        return res;
    }

二、滑动窗口

1004. 最大连续1的个数 III

给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。返回仅包含 1 的最长(连续)子数组的长度。
示例 1:
输入:A = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。

这道题最直观的解法就是依次尝试将K个0转换为1的后的子串长度,先固定子串左边位置left,然后滑动右窗口,遇到0则K--直到K为-1。但是这样的解法存在大量的重复计算,能否优化去掉一部分重复计算呢?

考虑到K为-1时,窗口内的0的数量为K,假设此时右指针位置为right,左指针为left,当left的下一位仍为1时,窗口内的0的数量没有变,所以连续长度肯定-1,所以不需要这部分的计算,因此下一次搜索时需要移动左指针来去掉一个0,这样就会减少不需要的重复计算。

    public int longestOnes(int[] A, int K) {
        int left = 0, right = 0;
        int len = A.length;
        int res = 0;
        while(left<=right&&right<len){
            if(A[right]==0)
                K--;
            if(K < 0){
                res = Math.max(right-left,res);
                while(A[left]!=0)
                    left++;
                left++;
                K++;
            }
            right++;
            

        }
        return Math.max(right-left,res);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章