题目要求
Given a list of non-negative numbers and a target integer k, write a function to check if the array has a continuous subarray of size at least 2 that sums up to a multiple of k, that is, sums up to n*k where n is also an integer.
Example 1:
Input: [23, 2, 4, 6, 7], k=6
Output: True
Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6.
Example 2:
Input: [23, 2, 6, 4, 7], k=6
Output: True
Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and sums up to 42.
Note:
- The length of the array won't exceed 10,000.
- You may assume the sum of all the numbers is in the range of a signed 32-bit integer.
假设有一个非负整数数组,要求找到其中一个连续的子数组,该子数组中所有元素的和是k的整数倍。要求连续子数组的长度大于等于2.
思路一:暴力循环
如果我们可以计算出所有长度大于等于2的连续子数组的元素和,就可以判断出是否存在满足题意的子数组。这里可以做一个简单的优化,即用一个额外的数组用来存[0..i]的子数组的元素和,则sum[i..j]的子数组的元素和可以通过sum[0..j+1]-sum[0..i-1]计算得出。代码如下:
public boolean checkSubarraySum(int[] nums, int k) {
if (nums == null || nums.length <= 1) {
return false;
}
int[] sum = new int[nums.length+1];
sum[0] = 0;
for (int i = 1 ; i=2 ; i--) {
for (int j = 0 ; j+i
思路二:记录中间结果
基于思路一的基础上可以进一步优化,既然我们要计算的是k的整数倍的子数组和,而我们有的是可以通过O(N)的时间复杂度计算出[0...i]子数组的元素和。那么我们可以推算出,假如sum[0..j]%k = m,而sum[0..i]%k = m(i-j>=2),那么可以知道[i+1, j]这个子数组一定是k的整数倍。因此我们只需要记录已经遍历过的从0下标开始的所有子数组的和以及对应的下标值,并且判断是否存在如上述的关联即可。
public boolean checkSubarraySum2(int nums[], int k){
Map map = new HashMap<>();
map.put(0,-1);
int sumSoFar = 0;
for(int i=0; i < nums.length; i++){
sumSoFar = sumSoFar + nums[i];
if(k != 0) sumSoFar = sumSoFar % k;
if(map.containsKey(sumSoFar)){
int start = map.get(sumSoFar);
if(i-start > 1) return true;
}else{
map.put(sumSoFar, i);
}
}
return false;
}
思路三:分治法
分治法在这题的核心思想在于,将整个数组先一分为二,分别判断在左子数组和右子数组中是否存在满足条件的子数组,如果没有,再判断跨左右子数组的子数组是否存在满足条件的连续子数组。分治法和动态规划都在于想通递归场景后,代码就行云流水了。
public boolean checkSubarraySum(int[] nums, int lo, int hi, int k){
if(lo==hi) return false;
int mid = lo+(hi-lo)/2;
if(checkSubarraySum(nums, lo, mid, k)) return true;
if(checkSubarraySum(nums, mid+1, hi, k)) return true;
int left = mid, right = mid+1;
int sum = nums[left];
while(left>=lo&&right<=hi){
sum += nums[right];
if((k>0&&sum%k==0)||(k==0&&sum==0)) return true;
--left;
++right;
if(left>=lo){
sum += nums[left];
if((k>0&&sum%k==0)||(k==0&&sum==0)) return true;
}
}
return false;
}
public boolean checkSubarraySum(int[] nums, int k) {
k = Math.abs(k);
return checkSubarraySum(nums, 0, nums.length-1, k);
}