Loading...

力扣P525. 连续数组、P523. 连续的子数组和

P525

原题链接 https://leetcode-cn.com/problems/contiguous-array/

解题思路

最近学的 前缀和

构造一个新数组 store 长度为原数组的 length+1
store[i+1] 存储的值是 原来数组中 0~ i 位置上的元素的和,举个例子:

  • 原数组nums为 [1,2,3,4,5]
  • 那么store数组 的值为 [0,1,1+2,1+2+3,1+2+3+4,1+2+3+4]

那么当我们要求原数组nums i到j所有元素的和,就可以 使用 store[j+1] - store[i]
举例说明:nums索引为 1~3的和为 2+3+4= store[4] - store[1]

有了前缀和之后,就可以子序列 i~j的元素求和 从O(n) 降到 O(1)

代码

class Solution {
    public int findMaxLength(int[] nums) {

        int[] store = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            store[i + 1] = nums[i] + store[i];
        }

        //从i~j之间的求和为 store[j]-store[i]


        //1、首先这个连续子数组为  偶数长 【0和1各一半,所以是偶数】
        //2、从最长的开始检查
        //如  0、1、1、1、1、0、0、0、0、0
        // 先假设  最长先为10, 索引为 0~9 的和看是否为  5

       //如果nums长度为偶数,则最大的长度为  length ,最大索引为 nums.length - 1
       //如果nums长度为奇数,则最大的长度为  length-1 ,最大索引为 nums.length - 2
       int first = (nums.length & 1) == 0 ? nums.length - 1 : nums.length - 2;

        for(int j=first;j>0;j=j-2){
            for(int i=0;i<nums.length-j; i++ ){
                //i ~ i+j 的元素的和 必须满足是 跨度的 一半
                if((store[i+j+1] - store[i])*2== j+1  )   
                    return j+1;
            }
        }
        return 0;

    }
}

P523

原题链接 https://leetcode-cn.com/problems/continuous-subarray-sum/

解题思路

这题挺折腾我也是看了很多题解写的,说实在的这道题的难度 应该是困难级才对,能直接自己想出O(n)的复杂度的该给困难级,这题复杂度O(n2)都会超时。你也可以看一下通过率。即便是我已经看明白了思路,自己coding也错误提交了三次

前缀和

关于前缀和 你首先应该 懂。 看 暴力+前缀和

同余定理

  x%k==a 且 y%k==a  //a为任意余数,包括0  
则  有 x-y 的绝对值为k的倍数

把x,y分别当做前缀和中的两个元素,则 如果要求(store[i]- store[j])%k==0store[i]%k== store[j]%k
那么就要求 前缀和数组store中是否能 找到两个数i,j 满足 store[i]%k== store[j]%k

HashMap

要找到两个数满足 store[i]%k== store[j]%k 的i,j 就转换成了类似 找相同两个数方法,正常为O(n2)的复杂度,但使用hashmap就能转换成O(n)的复杂度

官方题解比较晦涩的就是关于 map.put(0, -1);这句代码的。 原文写的是 规定空的前缀的结束下标为 -1,由于空的前缀的元素和为 0,因此在哈希表中存入键值对 (0,-1) 反正我开始理解了半天,懂不起。

后来自己想明白了。如果说前缀和数组store本身有一个元素i的值是 k的倍数,也就是说如果store[i]%k==0 代表 0i-1 索引元素的和 已经就是k的倍数了。

为了让代码更好理解我就改成了这样的

这样的效果就等同于map.put(0, -1)

  int y= store[i]%k;  //y为当前前缀和的余数

  //只要y==0 就代表 `0`到 `i-1` 索引元素的和 已经就是k的倍数了
  //注意 store[1]存储nums[0],此时 store只有一个元素,满足题目要求  子数组大小 至少为 2
  //所以 且 i>=2
  if(y==0&&i>=2)      
    return  true;

代码

class Solution {
  public boolean checkSubarraySum(int[] nums, int k) {
       if(nums==null|| nums.length<2)
            return false;
            
        //首先构造一个   前缀和 数组
        int [] store = new int[nums.length+1] ;

        for(int i=0;i<nums.length;i++){
            store[i+1]= store[i]+ nums[i];
        }
        //store[1]= nums[0]
        //store[2]=nums[0]+ nums[1]
        Map<Integer,Integer> map= new HashMap<>();

        for(int i=1;i<nums.length+1 ;i++){
           int y= store[i]%k;      //当前前缀和%k
           if(y==0&&i>=2)
                return  true;
            if( map.get(y)==null){  //如果没有相同余数的索引,则将当前余数的索引放入map
                map.put(y,i);
            }else if(i-map.get(y) >=2){ //找到了余数也为y的索引  为  map.get(y)
                                        //因为 子数组大小 至少为 2,索引索引差值为2
                return true;
            }
        }
        return false;
    }
}
posted @ 2021-06-05 20:02  倒霉蛋蛋虾  阅读(199)  评论(0编辑  收藏  举报