454.四数相加II
思路:nums1和nums2组成一个哈希表map1,key和value分别是两数之和,出现的次数
num3和num4也是如此,组成map2,在map2中寻找值为 0 - key(map1) 并将两个vulue相乘,累加得到最终结果
优化: map2无需进行存储,而是在遍历num3,num4时直接进行与map1中的key进行比较相加
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @param {number[]} nums3
* @param {number[]} nums4
* @return {number}
*/
var fourSumCount = function (nums1, nums2, nums3, nums4) {
// 将 nums1和nums2 的两数之和 存储一个map,将 nums3和nums4 存储一个map,key为两数之和,value为出现次数
// 再遍历map1 ,在map2中寻找是否有 对应相加为 0 的,将两个value 相乘,最后所有相加即为结果
let map1 = new Map()
let map2 = new Map()
let sum = 0
nums1.forEach(item1 => {
nums2.forEach(item2 => {
const sum = item1 + item2
map1.set(sum, map1.has(sum) ? map1.get(sum) + 1 : 1)
})
})
nums3.forEach(item1 => {
nums4.forEach(item2 => {
const sum = item1 + item2
map2.set(sum, map2.has(sum) ?map2.get(sum) + 1 : 1)
})
})
map1.forEach((value, key)=>{
const diff = 0 - key
if(map2.has(diff)){
sum += value * map2.get(diff)
}
})
return sum
}
// 改进,数组3,4的两数之和不需要在存储了,得到结果的时候进行sum+1就可以
var fourSumCount = function (nums1, nums2, nums3, nums4){
let map1 = new Map()
let res = 0
nums1.forEach(item1 => {
nums2.forEach(item2 => {
const sum = item1 + item2
map1.set(sum, map1.has(sum) ? map1.get(sum) + 1 : 1)
})
})
nums3.forEach(item1 => {
nums4.forEach(item2 => {
const sum = item1 + item2
res+=( map1.get( 0 -sum) || 0 )
})
})
return res
};
383. 赎金信
思路:将magazine字母作为key,出现的次数作为value存储为map,遍历ransomNote, 对map相应的key的value--,当value的值小于0,则false
改进:
字母一共只有26个,可以指定一个长度为26的数组,存储magazine中每个字母出现的顺序
遍历ransomNote时更改value
在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
/**
* @param {string} ransomNote
* @param {string} magazine
* @return {boolean}
*/
var canConstruct = function(ransomNote, magazine) {
if(ransomNote.length > magazine.length) return false
const record = new Array(26).fill(0)
const base = 'a'.charCodeAt()
for(let i = 0;i<magazine.length;i++){
record[magazine[i].charCodeAt() - base]++
}
for(let i = 0; i<ransomNote.length; i++){
record[ransomNote[i].charCodeAt() - base]--
if(record[ransomNote[i].charCodeAt() - base] < 0){
return false
}
}
return true
};
15. 三数之和
思路;
因为不需要返回下标,进行排序后,在for循环中通过双指针遍历,可以将O(n)变成(n)
不用哈希表的原因:
通过两层for循环可以确定a,b然后通过哈希法来确定 0-(a+b) 是否在 数组里出现过,这里是没问题的,但是不能返回重复的集合,去重是非常麻烦的
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
const res = [], len = nums.length
// 将数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < nums.length -2; i++) {
let l = i + 1, r = nums.length - 1, iNum = nums[i]
if (iNum > 0) return res
// 这里是对i的去重
if (iNum === nums[i - 1]) continue
while (l < r) {
let lNum = nums[l], rNum = nums[r], threeSum = iNum + lNum + rNum
if (threeSum > 0) {
r--
} else if (threeSum < 0) {
l++
} else {
res.push([iNum, lNum, rNum])
// 对l去重
while (l < r && nums[l] === nums[l + 1]) {
l++
}
// 对r去重
while (l < r && nums[r] === nums[r - 1]) {
r--
}
l++
r--
}
}
}
return res
};
18. 四数之和
思路和三数之和一样,多一层for循环
需要注意的是,j > i+1的时候才进行比较去重,要保证每次i循环的时候第一个j可以执行循环
/**
* @param {number[]} nums
* @param {number} target
* @return {number[][]}
*/
var fourSum = function(nums, target) {
let res = []
nums.sort((a,b)=>a-b)
// 这还不能这么写,因为全是负的,越加越小
// if(nums[0] > target) return res
for(let i = 0;i<nums.length -3;i++){
let j = i+1
if(nums[i] === nums[i-1]) continue
for(;j<nums.length;j++){
// 这里要增加j>1的条件,确保每次i循环的时候,j第一次能执行
if(j > i+1 && nums[j] === nums[j-1]) continue
let l = j+1,r=nums.length -1, jNum = nums[j]
while(l<r){
let fourSum = nums[i] + jNum + nums[l] + nums[r]
if(fourSum < target){
l++
}else if(fourSum > target){
r--
}else{
res.push([nums[i] , jNum , nums[l],nums[r]])
while(l < r && nums[l+1] === nums[l]){
l++
}
while(l < r && nums[r-1] === nums[r]){
r--
}
l++
r--
}
}
}
}
return res
};