题一:四数相加
题目链接:四数相加
解题思路: 题目给出四个数组,要求在这个数组中分别取一个数使得nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0,返回满足此要求元组的个数,注意这里不要求去重,所以我们只管遍历即可,我首先想到先遍历num1和nums2,把这俩数之和放置一个map当中,在设置一个count记录目标元组个数,把num1+num2当key,把出现次数做value,然后再去遍历num2和num3,在我们之前设置过的数组中查找,此时key = 0 - (num3[k] + num4[I]),如果存在直接累加至count变量即可,根据以上要点此题可解。
解题代码:
var fourSumCount = function (nums1, nums2, nums3, nums4) {
// 先算nums1 + nums2
let tagMap = new Map();
let count = 0;
for (let i = 0; i < nums1.length; i++) {
for (let j = 0; j < nums2.length; j++) {
let key = nums1[i] + nums2[j];
if (tagMap.has(key)) {
let value = tagMap.get(key) + 1;
tagMap.set(key, value);
} else {
tagMap.set(key, 1)
}
}
}
console.log(tagMap)
for (let i = 0; i < nums3.length; i++) {
for (let j = 0; j < nums4.length; j++) {
let key = 0 - (nums3[i] + nums4[j]);
if (tagMap.has(key)) {
count += tagMap.get(key);
}
}
}
return count;
};
题二:赎金信
题目链接:赎金信
解题思路: 题目给出连个字符串,问第一个字符串是否可由第二个字符串的字母组成,且第二个字符串的每个字符只能用一次,我的思路是利用map解决,遍历第二个字符串,把字符当作key,出现次数作为value存入,再遍历第一个字符串,再遍历时,如果在我们设定的map中查找到了此字符的记录的话则将该键对应的键值-1,如果没有找到或者是次value===0,直接返回false。最后如果一切顺利则返回true。
解题代码:
var canConstruct = function (ransomNote, magazine) {
let map_magz = new Map();
for(let i = 0; i < magazine.length;i++){
if(map_magz.has(magazine[i])){
let value = map_magz.get(magazine[i]) + 1;
map_magz.set(magazine[i],value)
}else{
map_magz.set(magazine[i],1)
}
}
for (let i = 0; i < ransomNote.length; i++) {
if (!map_magz.has(ransomNote[i]) || map_magz.get(ransomNote[i]) <= 0) {
return false;
} else {
let value = map_magz.get(ransomNote[i]) - 1;
map_magz.set(ransomNote[i],value)
// console.log(map_magz)
}
}
return true;
};
题三:三数之和
题目链接:三数之和
解题思路: 题目给定一个数组,要求出nums[i] + nums[j] + nums[k] = 0的时候的所有元组,且元组不能重复,这里就涉及到了一个去重的操作,一般会想到用哈希表来做,可是在这里用哈希去重的逻辑太复杂了(其实是我懒得看了),就不做解释,直接用卡哥推荐的三指针法,数组要先排序,方变我们后面的操作,遍历数组,初始时把数组的第一项设置为i=0,left=i+1,right=nums.length -1,这里的主要思想就是数组数值从下到大排列,我们可以根据这个特性来移动指针,如果三数之和大于0,right向左移动,如果三数之和小于0,left向右移动,这里还有一个特别重要的点就是去重的操作,先对于i,if(i>0&&nums[i] < nums[i-1) 则return,为什么是i-1而不是i+1呢,在这个场景之下,i-1的位置正是我们遍历过的,如果还算进去的话,一旦后面存在满足题目条件的情况,也就是三数相加等于0,这肯定是重复了的,而i+1的位置则是未遍历的元素,对于后面的情况我们不好判断。还有就是对left和right进行去重,在进入左右指针的移动循环时,left与left+1的值相等,left++,rigth与其左边的值相等,right–。
解题代码:
var threeSum = function (nums) {
// 先排序
nums = nums.sort((a, b) => a - b);
let result = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] > 0) return result;
// 对第一个数进行剪枝操作
if (i !== 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
while (left < right) {
let sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++
} else {
let temp = [];
temp.push(nums[i]);
temp.push(nums[left]);
temp.push(nums[right]);
result.push(temp);
// 对left 和 right进行剪枝操作
while (nums[left] === nums[left + 1]) {
left++;
}
while (nums[right] === nums[right - 1]) {
right--;
}
left++;
right--;
}
}
}
return result;
};
4 四数之和
题目链接: 四数之和
解题思路: 跟三数之和类似,这里我们设置四个指针,k,i,left,right,循环遍历k、i,根据k、i移动left、right,初始时k=0,i=k+1, left = i+1, right= nums.length -1,去重剪枝操作与三数之和类似。
解题代码:
var fourSum = function (nums, target) {
let result = [];
nums = nums.sort((a, b) => a - b);
if (nums.length < 4) return result;
for (let k = 0; k < nums.length; k++) {
if (nums[k] > target && nums[k] > 0) continue;
if (k > 0 && nums[k] === nums[k - 1]) continue;
for (let i = k + 1; i < nums.length; i++) {
if (i > k + 1 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
while(left < right){
let sum = nums[k] + nums[i] + nums[left] + nums[right];
if(sum > target){
right--;
}else if(sum < target){
left++;
}else{
let temp = [];
temp.push(nums[k]);
temp.push(nums[i]);
temp.push(nums[left]);
temp.push(nums[right]);
result.push(temp);
while(nums[left] === nums[left+1]){
left++;
}
while(nums[right === nums[right-1]]){
right--;
}
left++;
right--;
}
}
}
}
return result;
};