背景
放些卡的题目,和逐步改进的思路和策略以及测试结果。
3sum
solution 1: Time Limit Exceeded
315 / 318 test cases passed. 折戟沉沙的数据集地址
本地跑了下,用时50-60s,最终得到的数据个数是16258,考虑到这个solution 1 毕竟通过了绝大部分测试集,姑且当作是这个数据集的正确个数。
var threeSum = function (nums) {
let result = [];// 用来返回最终结果
let capture = [];// 存放数据的快照,用来排除相同组合。
// 循环嵌套,第一个int
for (let i = 0; i <= nums.length; i++) {
// 循环嵌套,第二个int
for (let j = 0; j <= nums.length; j++) {
// i,j的value可以相等,但是index是不能一样
if(i != j ){
// 最后一个int是 i,j对应数据的负数
let kValue = 0 - nums[i]-nums[j];
// 找到k的index
let k = nums.indexOf(kValue);
// k必然是存在,且不能和i,j相同
if (k>=0 && k != i && k!=j){
// 找到一个子集
let item = [nums[i],nums[j],nums[k]];
// 对子集进行排序和tostring操作,存作快照
if(capture.indexOf(item.sort().join(",")) == -1){
// 且快照中不存在该string,才进行push。
capture.push(item.sort().join(","));
// 同时作为一个真 子集,push到最后要返回的array中。
result.push(item);
}
}
}
}
}
// 全部暴力循环后,返回。
return result;
}
通过now-mdn可以测试每一步所花费的时间。通过一些测试,会发现问题是出在for循环上,因此思路要换换。
solution 2: Runtime Error
然后我开始考虑,要不要试着当做数学题来看。然后也报错了,淦。但是数据个数和上面一样。折在同一个数据集,但是本地能跑出来,时间是10s的样子。 而且跑出来的数据集个数也是一样的:16258
思路倒是也简单,就是先进行sort
- 作为一个全是数字的数组,需要挑选3个数字达到和值为0的结果,那么就是3种情况:【负数,0,正数】,【负数,正数,正数】,【负数,负数,正数】, 当且仅当0有三个以上,可以多一个特殊情况【0,0,0】
- 决定先把排序后arr中第一个0和最后一个0的位置找到,这样可以知道有没有特殊情况。
- 先整体来说,【负数,正数,正数】,【负数,负数,正数】这两种情况是有没有0,都会需要做的。所以考虑抽成一个函数。
- 开始判断0的个数,如果是>=3个,就先把特殊【0,0,0】放进去。
- 只要有0,【负数,0,正数】这样的就需要去找找看。
- 有0无0,影响的其实是sort后的arr的正负数index区间。因此再次调用函数。
var threeSum = function (nums) {
let result = [];
// 这个函数是在a中找两个数,再确认他们和的相反数是否在b中
function FindOut(a, b) {
var c2a = a.flatMap( //从参数a这个arr入手,排列组合,从里面选2个(index)数,就会变成一个新的arr
(v, i) => a.slice(i + 1).map(w => v + "," + w + "," + (v + w)) //这个可以得出三个值,“加数1,加数2,加数1+加数2”
);
// 做成了["1,1,2",“1,2,3”...]的原因就是为了去重,因为数组直接去重不行,也不知道为啥,字符串就行。
addend = [... new Set(c2a)];
// 然后会考虑,“num1,num2,num3”split过,拿到没有“,”的,再变为int
for (let i in addend) {
let item = addend[i].split(",");
let last = -parseInt(item[2]);
if (b.find(element => element == last)) {
result.push([parseInt(item[0]), parseInt(item[1]), last]);
}
}
};
nums.sort(function (a, b) { return a - b });// 将nums 按着int从小到大排序。这个用时大概是1-2ms,非常小,也就是build-in 函数只作一层其实非常快。
var zero0 = nums.indexOf(0); // 找到第一个0的index
var zero1 = nums.lastIndexOf(0); //最后一个0 的index
//step 1 ,当数组没有0的时候,其实只用考虑:【负数,正数,正数】,【负数,负数,正数】这两种情况。
if (zero0 === -1) {
var minPositiveIndex = nums.findIndex(element => element > 0); // 返回的是该正数的index,如果找不到,那么这个值会被定成-1
if (minPositiveIndex != -1) { // 整个array确实有正数:
//截取nums array的正整数集合:
var allPositives = nums.slice(minPositiveIndex); //正数
var allNegatives = nums.slice(0, minPositiveIndex); // 负数
FindOut(allPositives, allNegatives);
FindOut(allNegatives, allPositives);
};
}
// step 1 over
// step 2
if (zero0 != -1 && zero1 != -1 && zero1 - zero0 >= 2) { //前后同时存在0,且至少有3个0,
result.push([0, 0, 0]);
};
// step 2 over
// step 3 when nums have 0.
if (zero0 != -1 && zero1 != -1) { //也许是1,2,3....个0,但是不管多少个0,在【负数,0,正数】的场景下都是一个0
var allNegatives = nums.slice(0, zero0);//所有的负数array
var allPositives = nums.slice(zero1 + 1);//所有的正数array
// step 3.1考虑处理【负数,0,正数】这个集合
// 由于这一步只考虑[-,0,+] 这样的相反数组合,因此会想去重处理一下:
allNeSingle = [...new Set(allNegatives)];
allPoSingle = [...new Set(allPositives)];
for (let i in allNeSingle) { // 对于【负数,0,正数】这样的组合,找一边就可以了。
let lastIndex = allPoSingle.indexOf((-allNeSingle[i])); //在正数集合里找,负数,对应相反数的index,
if (lastIndex != -1) { // 找到了index
let item = [allNeSingle[i], 0, allPoSingle[lastIndex]];
result.push(item);// store the item as a reslut element
}
};
// step 3.1 over
// step 3.2 start
// 【负数,正数,正数】,【负数,负数,正数】,在有0的情况下去截取。
FindOut(allPositives, allNegatives);
FindOut(allNegatives, allPositives);
// step 3.2 over
};
// step3 完结。
return result;
}
搜了下,这个错误一般都是内存用太多,然后又搜了下js咋看内存消耗:Analyze runtime performance
就用太多了,整整430MB,真可怕。看了眼答案人家都是50MB左右。
淦,忧郁了,暂时不做题了