P525
解题思路
最近学的 前缀和
构造一个新数组 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==0
则 store[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 代表 0
到 i-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;
}
}