题目描述
给定一个整数数组 nums 和一个正整数 k,请找出数组中是否存在一个连续的子数组,其元素之和是 k 的倍数(即 sum % k == 0)。要求子数组至少包含两个元素。如果存在,返回 true;否则,返回 false。
输入与输出
输入: 一个整数数组 nums,一个正整数 k。
输出: 一个布尔值,表示是否存在符合条件的连续子数组。
样例
输入:nums = [23, 2, 4, 6, 7], k = 6
输出:true
解释:[2, 4] 的和为 6,是 k 的倍数
输入:nums = [23, 2, 6, 4, 7], k = 13
输出:false
输入:nums = [23, 2, 6, 4, 7], k = 42
输出:true
解释:[23, 2, 6, 4, 7] 的和为 42,是 k 的倍数
关键思路
为解决这个问题,我们需要从数学和算法的角度深入分析问题的核心:
-
数学背景:前缀和与模运算
- 假设一个子数组
nums[i:j]的和是sum,且满足sum % k == 0,那么数组中某两个前缀和的模值必定相等。 - 假设前缀和在索引
i处为prefix_sum[i],在索引j处为prefix_sum[j],如果prefix_sum[i] % k == prefix_sum[j] % k且j - i > 1,那么子数组nums[i+1:j]的和是k的倍数。
- 假设一个子数组
-
算法设计:哈希表记录模值出现的位置
- 用一个哈希表记录每个前缀和对
k取模的值及其最早出现的索引。 - 遍历数组时,计算当前前缀和的模值,并检查是否已经出现在哈希表中:
- 如果存在,且当前索引与之前索引的差值大于等于 2,则返回
true。 - 如果不存在,将当前模值和索引存入哈希表。
- 如果存在,且当前索引与之前索引的差值大于等于 2,则返回
- 用一个哈希表记录每个前缀和对
-
特殊情况处理:
k == 0- 如果
k == 0,题目要求我们检查是否存在两个连续的0,因为只有连续的0才能满足条件。
- 如果
代码实现
以下是完整代码实现:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static boolean checkSubarraySum(int[] nums, int k) {
// 哈希表存储模值及其索引
Map<Integer, Integer> prefixSumMod = new HashMap<>();
prefixSumMod.put(0, -1); // 初始模值为0,对应索引为-1
int totalSum = 0;
for (int i = 0; i < nums.length; i++) {
totalSum += nums[i]; // 更新前缀和
if (k != 0) {
totalSum %= k; // 取模值
}
// 检查当前模值是否已出现
if (prefixSumMod.containsKey(totalSum)) {
// 如果索引差大于等于2,返回true
if (i - prefixSumMod.get(totalSum) > 1) {
return true;
}
} else {
// 否则记录当前模值及其索引
prefixSumMod.put(totalSum, i);
}
}
return false; // 未找到符合条件的子数组
}
public static void main(String[] args) {
System.out.println(checkSubarraySum(new int[]{23, 2, 4, 6, 7}, 6)); // true
System.out.println(checkSubarraySum(new int[]{23, 2, 6, 4, 7}, 13)); // false
System.out.println(checkSubarraySum(new int[]{23, 2, 6, 4, 7}, 42)); // true
}
}
代码解释
-
初始化哈希表
- 使用
prefixSumMod.put(0, -1)初始化模值 0 的索引为 -1,方便处理从头开始的子数组。
- 使用
-
累积前缀和并取模
totalSum += nums[i]计算前缀和。- 如果
k != 0,则取模,计算当前前缀和的模值。
-
模值重复判断
- 如果当前模值已出现在哈希表中,并且索引差大于等于 2,则返回
true。
- 如果当前模值已出现在哈希表中,并且索引差大于等于 2,则返回
-
记录模值和索引
- 如果当前模值尚未出现在哈希表中,则记录该模值和索引。
知识总结
在这道题中,我学习到了以下关键知识点:
-
前缀和的应用
- 前缀和是一种非常有用的技巧,可以快速计算数组的区间和。在这道题中,结合模运算,进一步提升了它的应用范围。
-
哈希表的优化作用
- 通过哈希表存储模值及其最早出现的索引,能够在常数时间内判断是否存在符合条件的子数组,从而将时间复杂度从暴力解法的 (O(n^2)) 优化到 (O(n))。
-
特殊情况处理
- 当
k == 0时,需要特别考虑连续的0。
- 当
小结
通过这道题,我深刻体会到数学与编程结合的魅力。利用前缀和与模运算的性质,再通过哈希表优化算法,可以用极低的时间复杂度解决看似复杂的问题。希望这篇文章对大家有所帮助!一起加油!