一、题目
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:
子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。
如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。
二、思路
1)暴力法(超出时间限制)
求解所有长度的连续子数组之和,查看是否能够被k整除。
2)前缀和+哈希表
其实就是需要找到是否存在这样的连续子数组,连续子数组问题,可以联想到前缀和,使用前缀和可以在O(1)时间求和。
再观察一下能够被k整除的特性,就是num%k==0才可以,那么前缀和表达式为sum[j](代表从[0, j-1]区间所有元素的和),可以获得区间[i, j]的和为sum[j]-sum[i],只有当:
(
s
u
m
[
j
]
−
s
u
m
[
i
]
)
%
(
k
)
=
0
,
即
s
u
m
[
j
]
%
k
=
s
u
m
[
i
]
%
k
(sum[j] - sum[i])\%(k)=0,即sum[j]\%k=sum[i]\%k
(sum[j]−sum[i])%(k)=0,即sum[j]%k=sum[i]%k
根据sum[j]%k的值去寻找是否存在sum[i]%k,存在就代表有以位置j结尾的连续子数组,其和为k倍数。
三、代码
1)暴力法(超出时间限制)
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
if (nums.length < 2) {
return false;
}
int[] memo = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
memo[i] = nums[i];
}
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < nums.length - i; j++) {
memo[j] += nums[j + i];
if (memo[j] % k == 0) {
return true;
}
}
}
return false;
}
}
时间复杂度为O(n),空间复杂度为O(n)。
2)前缀和+哈希表
class Solution {
public boolean checkSubarraySum(int[] nums, int k) {
int[] sum = new int[nums.length + 1];
for (int i = 1; i < sum.length; i++) {
sum[i] = sum[i-1] + nums[i-1];
}
Set<Integer> set = new HashSet();
for (int i = 2; i < sum.length; i++) {
set.add(sum[i-2]%k); // 考虑到连续子数组长度至少为2
if (set.contains(sum[i]%k)) {
return true;
}
}
return false;
}
}
时间复杂度为O(n),空间复杂度为O(n)。