454. 四数相加
链接
题目链接
文章链接
第一想法
查看是否和为0,用于查找,所以第一想到了哈希表,用map还是set,因为数字之和可能有多种组合,所以用map,可以方便记录和数字出现的次数。同时要写两个两层的for循环,这样的时间复杂度是,如果一个写三层for循环,一个写一层,那么时间复杂度为,所以写两个两层的for循环,代码如下:
function fourSumCount(nums1: number[], nums2: number[], nums3: number[], nums4: number[]): number {
let map:Map<number, number>=new Map()
let res:number=0
for(let i=0;i<nums1.length;i++){ //用于存储nums1和nums2数组两个数字相加之和
for(let j=0;j<nums2.length;j++){
if(map.has(nums1[i]+nums2[j])){
map.set(nums1[i]+nums2[j],map.get(nums1[i]+nums2[j])+1) //记录和相同时出现的次数
}else{
map.set(nums1[i]+nums2[j],1)
}
}
}
for(let i=0;i<nums3.length;i++){
for(let j=0;j<nums4.length;j++){
if(map.has(0-(nums3[i]+nums4[j]))){//查询是否四个数加起来为0
res+=map.get(0-(nums3[i]+nums4[j]))
}
}
}
return res
};
看完文章后的想法
文章的思路总体上和自己的想法是一致的,所以在此不进行再次赘述,其他语言代码可以查看代码随想录
思考
这道题不难,唯一需要想清楚的是要这道题是要找到等于0的个数,而不是要找出这四个的下标是多少,所以不需要四层for循环,挨个尝试是否为0.只需要找到数字相加之和是否为0就可以了,为了使时间复杂度最低,四数相加可以转换为二数相加就可以完美解决这道题。
383. 赎金信
链接
题目链接
文章链接
第一想法
这道题和242有效的字母异位词类型差不多,所以一下子就想到了用哈希表,这道题用数组,因为只有26个字母,很简单,唯一的区别是magazine中的字母要包含ransomNote的字母,也就是ransomNote是magazine的子集,代码如下:
function canConstruct(ransomNote: string, magazine: string): boolean {
let arr:number[]=new Array(26).fill(0)
for(let i=0;i<ransomNote.length;i++){ //先循环哪个要看最终判断条件是哪个
arr[ransomNote[i].charCodeAt(0)-'a'.charCodeAt(0)]++
}
for(let i=0;i<magazine.length;i++){
arr[magazine[i].charCodeAt(0)-'a'.charCodeAt(0)]--
}
return !arr.some(item=>item>0) //先循环ransomNote,如果循环完magazine后,arr中存在大于0的数,说明ransomNote中的某个字母个数大于magazine中对应字母的个数
};
看完文章后的想法
文章的想法和自己的想法几乎是完全一致的,只不过文章先循环的是magazine,所以最终条件也不一样,下列代码为代码随想录中的代码:
function canConstruct(ransomNote: string, magazine: string): boolean {
let helperArr: number[] = new Array(26).fill(0);
let base: number = 'a'.charCodeAt(0);
let index: number;
for (let i = 0, length = magazine.length; i < length; i++) {
helperArr[magazine[i].charCodeAt(0) - base]++;
}
for (let i = 0, length = ransomNote.length; i < length; i++) {
index = ransomNote[i].charCodeAt(0) - base;
helperArr[index]--;
if (helperArr[index] < 0) {
return false;
}
}
return true;
};
思考
这道题的主要思路和242有效的字母异位词相似,只不过一个是子集,应该是相等,所以也没有多思考时间按,这道题10多分钟就搞定了,还是比较简单的。
15. 三数之和
链接
文章链接
题目链接
第一想法
第一感觉这不是和四数相加2很像,但是发现结果不同,而且还要去重,有点麻烦,但是还是决定用哈希算法尝试一下(去重就是麻烦),代码如下:
function threeSum(nums: number[]): number[][] {
let map1:Map<number,number[][]>=new Map() //用来存储元素
let map2:Map<number,number[][]>=new Map() //用来存储下标
let set=new Set()
let res=[]
for(let i=0;i<nums.length;i++){
for(let j=i+1;j<nums.length;j++){
if(map1.has(nums[i]+nums[j])){
map1.get(nums[i]+nums[j]).push([nums[i],nums[j]])
map2.get(nums[i]+nums[j]).push([i,j])
map1.set(nums[i]+nums[j],map1.get(nums[i]+nums[j]))
map2.set(nums[i]+nums[j],map2.get(nums[i]+nums[j]))
}else{
map1.set(nums[i]+nums[j],[[nums[i],nums[j]]])
map2.set(nums[i]+nums[j],[[i,j]])
}
}
}
for(let i=0;i<nums.length;i++){
if(!map1.has(0-nums[i])) continue
let arr:number[][]= map2.get(0-nums[i])
let arr1:number[][]= map1.get(0-nums[i]) //元素
for(let j=0;j<arr.length;j++){
if(arr[j].includes(i)) continue
let arr2:number[]=Array.from(arr1[j])
arr2.push(nums[i])
if(!set.has(arr2.sort().join(" "))){ //这里是用来去重的
set.add(arr2.sort().join(" "))
res.push(Array.from(arr2))
}
}
}
return res
};
结果运行,发现——寄,通过不了,感觉是内存太大了。不过也通过了308个测试用例。
看完文章后的想法
看完文章后,给我的感觉是,没想到,这里还能用双指针,没想到。但是这道题的重点是,要提前把数组排序用于去重!!!!这个是最重要的,但是我没想到,建议看看代码随想录的这篇文章,确实好。代码如下:
function threeSum(nums: number[]): number[][] {
nums.sort((a,b)=>a-b) //进行排序
let res:number[][]=[]
//要满足 a+b+c==0
for(let i=0;i<nums.length;i++){
if(nums[i]>0) return res //因为排序后,如果第一个数大于0,则相加肯定大于0
if(i>0&&nums[i]==nums[i-1]) continue //对a去重
let left:number=i+1
let right:number=nums.length-1
while(right>left){
if(nums[i]+nums[left]+nums[right]>0) right--
else if(nums[i]+nums[left]+nums[right]<0) left++
else{
res.push([nums[i],nums[left],nums[right]])
left++
right--
while(nums[right]==nums[right+1]) right-- //对c去重
while(nums[left]==nums[left-1]) left++//对b去重
}
}
}
return res
};
思考
通过这道题我发现了个误区,如果题目属于哈希表这个范围的话,我就只会按照哈希表来想,不会从其他的地方来想,这道题如果用双指针来想,确实简单但是要想清去重的思路以及剪枝。
18. 四数之和
链接
文章链接
题目链接
第一想法
刚刚做完三数之和,所以第一眼决定不用哈希表,决定用双指针,唯一的区别就是不需要判断nums[i]>target了,因为target可能是负数,代码如下:
function fourSum(nums: number[], target: number): number[][] {
let res:number[][]=[]
nums.sort((a,b)=>a-b)
//要满足a+b+c+d==0
for(let i=0;i<nums.length;i++){
//这里不能有nums[i]>target了,因为target可能是负数
if(i>0&&nums[i]==nums[i-1]) continue
for(let j=i+1;j<nums.length;j++){
//这里要判断j>i+1的原因是防止nums[i]==nums[j]时这种情况被去除
if((j-i)>1&&nums[j]==nums[j-1]) continue
let left:number=j+1
let right:number=nums.length-1
while(right>left){
if(nums[i]+nums[j]+nums[left]+nums[right]>target) right--
else if(nums[i]+nums[j]+nums[left]+nums[right]<target) left++
else {
res.push([nums[i],nums[j],nums[left],nums[right]])
left++
right--
while(left<right&&nums[left]==nums[left-1]) left++
while(left<right&&nums[right]==nums[right+1]) right--
}
}
}
}
return res
};
看完文章后的想法
文章和我用的方法一样,想法也类似,下面为代码随想录的代码:
function fourSum(nums: number[], target: number): number[][] {
nums.sort((a, b) => a - b);
let first: number = 0,
second: number,
third: number,
fourth: number;
let length: number = nums.length;
let resArr: number[][] = [];
for (; first < length; first++) {
if (first > 0 && nums[first] === nums[first - 1]) {
continue;
}
for (second = first + 1; second < length; second++) {
if ((second - first) > 1 && nums[second] === nums[second - 1]) {
continue;
}
third = second + 1;
fourth = length - 1;
while (third < fourth) {
let total: number = nums[first] + nums[second] + nums[third] + nums[fourth];
if (total === target) {
resArr.push([nums[first], nums[second], nums[third], nums[fourth]]);
third++;
fourth--;
while (nums[third] === nums[third - 1]) third++;
while (nums[fourth] === nums[fourth + 1]) fourth--;
} else if (total < target) {
third++;
} else {
fourth--;
}
}
}
}
return resArr;
};
总结
今天前两道题比较简单,但是第三道题比较难,没有想到,同时尝试用了哈希表来解决三数之和,发现能写,但是非常难写,去重比较麻烦。第三题写完后再写第四题,会立即想到用双指针,说明对于三数、四数、五数等都可以用双指针,就能用时四小时.