leetcode 每日一题 523. 连续的子数组和
我的思路
- 遍历数组,遍历以第 i 个元素为起点的所有子数组,用 temp 记录当前的总和,并计算是否为 k 的倍数,如果是则返回 true;否则,遍历结束后返回 false。
时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)只是 temp 占用了空间。
93 / 94 个通过测试用例。 最后一个果然超时了。
var checkSubarraySum = function (nums, k) {
for (let i = 1; i < nums.length; i++) {
temp = nums[i] + nums[i - 1];
if (temp % k == 0) {
return true;
} else {
for (let j = i + 1; j < nums.length; j++) {
temp += nums[j];
if (temp % k == 0) {
return true;
}
}
}
}
return false;
};
- 前缀和,想了想前缀和还是要遍历啊,这可怎么办,还是超时。
var checkSubarraySum = function (nums, k) {
var prefix = [];
prefix[0] = nums[0];
for (let i = 1; i < nums.length; i++) {
prefix[i] = prefix[i - 1] + nums[i];
if (prefix[i] % k == 0) {
return true;
}
}
for (let i = nums.length - 1; i >= 0; i--) {
for (let j = 0; j <= i - 2; j++) {
if ((prefix[i] - prefix[j]) % k == 0) {
return true;
}
}
}
return false;
};
看看题解
官方题解也确实用到了前缀和的方法。
官方题解的思路是:如果两个前缀和除以 k 的余数相等,那么这两个前缀和的差一定是 k 的倍数。此时只要两个前缀和并不相邻,即满足子数组大小至少为 2,且子数组元素总和为 k 的倍数。
/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var checkSubarraySum = function (nums, k) {
const map = new Map();
map.set(0, -1);
var temp = 0;
for (let i = 0; i < nums.length; i++) {
temp = (temp + nums[i]) % k;
if (map.has(temp)) {
var pre = map.get(temp);
if (i - pre >= 2) {
return true;
}
} else {
map.set(temp, i);
}
}
return false;
};
总结
-
有一种能做出来是你觉得你能做出来。其实仔细思考下来需要避开的坑非常多。就比如本题中的前缀和,知道要用前缀和来节约时间,但是没有细想怎么实现,那其实简单实现了之后才发现并没有达到节约时间的目的。
-
学到了 JS 的 hash 表 Map() 的基本使用方法
创建 map 对象
var map = new Map([ ["key1", value1], ["key2", value2], ]);
将键值对放入 map 对象
map.set("key3", value3);
根据 key 获取 map 值
map.get("key1");
查看 map 中是否有给定的 key
map.has("key"); //false
删除 map 指定对象
map.delete("key1");
循环遍历 map
map.forEach(function (key) { console.log("key", key); });