书接上文哈希表①,哈希表中最经典的题目就是两数之和,这个拥有编号1的题目,相信大家都做过。本文将补充使用哈希表的一些题目。
LeetCode-1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
分析:我们需要在数组中找到两个数的和为target的下标,首先想暴力做法:
//暴力解法
var twoSum = function (nums, target) {
const res = [];
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
res.push(i);
res.push(j);
break;
}
}
}
return res;
};
内层循环就是在数组i后寻找能够与i处值之和为target的值,实际上就是想知道当前数组中是否存在另一个数(target-nums[i])。
发现了吗?想快速知道一个数是否存在时,我们应该想到哈希表(这是我们上一篇提到的) ok,那我们试试:
var twoSum = function (nums, target) {
//存入map
const map = new Map();
const res = [];
for (let i = 0; i < nums.length; i++) {
map.set(nums[i], i);
}
for (let i = 0; i < nums.length; i++) {
if (map.has(target - nums[i]) && i !== map.get(target - nums[i])) {
res.push(i);
res.push(map.get(target - nums[i]));
break;
}
}
return res;
};
你如果如上面的思路写的话,就会写出上述代码,你需要将当前nums全部存入map(键是nums[i]的值,值是i),并且在循环时,你需要判断的不仅是是否有target-num[i],还需要判断索引不能相同。
此时就应该换个思路了,我们创建一个map,当遍历一个值后存入map,等于每次寻找另一半时,是在已经遍历过的值中寻找,这样可以节省空间。
var twoSum = function (nums, target) {
const curMap = new Map();
for (let i = 0; i < nums.length; i++) {
if (curMap.has(target - nums[i])) {
return [curMap.get(target - nums[i]), i];
}
curMap.set(nums[i], i);
}
};
这道题的思路是:利用map存储当前遍历过的值及其下标,每当遍历一个新元素时,我们就假定该元素满足条件,在已经知道的map中寻找他的另一半,即确定一个数,寻找另一个已知数的位置。
LeetCode-15.三数之和与LeetCode-18.四数之和并不能很好的使用哈希表,会超时,并且由于重复项的限制,剪枝操作及其复杂,需要使用排序+双指针,因此这里不做赘述,之后会在双指针中补充。
再来看看下面这个题!
LeetCode-454.四数相加 II
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < nnums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
我们只需要在四个数组中找到能够和为0的组合,每组的下标不能重复,但是对应的元素可以重复。思路是将nums1与nums2中的所有可能计算出来,并将其存储到map中,这样方便与我们后续获取是否存在,又因为可以允许重复的数字,所以两个数组中可能不同的下标计算出相同的值,因此我们用map的键存储和,值存储出现的次数。
接着列举另外两个数组的所有组合,如果map中存在,就说明可以获得x组正确的数据,其中x就是map中对应的值(出现的次数)
var fourSumCount = function (nums1, nums2, nums3, nums4) {
let count = 0;
let map = new Map();
for (const num1 of nums1) {
for (const num2 of nums2) {
map.set(num1 + num2, (map.get(num1 + num2) || 0) + 1)
}
}
for (const num3 of nums3) {
for (const num4 of nums4) {
if (map.has(-(num3 + num4))) count += map.get(-(num3 + num4))
}
}
return count;
};
总结
在我们刷题过程中,很多题目都会使用到哈希表,只要当我们需要对某个元素进行计数或需要快速获取某个元素是否存在时,都应该考虑使用哈希表来完成。