学习目标:
- 做完四道算法题,并根据视频和文章巩固哈希表理论基础
学习时间:
- 晚上七点-晚上十点
454.四数相加II
建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说我们都是舍空间 换时间, 工业开发也是这样。
思路:道题目是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!
代码实现:
var fourSumCount = function (nums1, nums2, nums3, nums4) {
var map = new Map()
let count = 0;
for (const n1 of nums1) {
for (const n2 of nums2) {
const sum = n1 + n2
map.set(sum, (map.get(sum) || 0) + 1)
}
}
for (const n3 of nums3) {
for (const n4 of nums4) {
const sum = n3 + n4
count += (map.get(0 - sum) || 0)
}
}
return count
};
383. 赎金信
建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展题
思路:这道题目是求 字符串a能否组成字符串b,而不用管字符串b 能不能组成字符串a。
本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。
-
第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用。
-
第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要
代码实现:
var canConstructHash = function (ransomNote, magazine) {
let record = new Array(26).fill(0),
base = "a".charCodeAt();
for (const m of magazine) {
record[m.charCodeAt() - base]++
}
for (const r of ransomNote) {
const index = r.charCodeAt() - base
//如果没有记录过 直接就是错的
if (!record[index]) {
return false
}
record[index]--
}
return true
}
15. 三数之和
建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦
代码实现:
var threeSum = function (nums) {
const res = [], len = nums.length
// 将数组排序
nums.sort((a, b) => a - b)
for (let i = 0; i < nums.length; i++) {
let l = i + 1, r = len - 1, iNums = nums[i]
if (nums[i] > 0) {
return res
}
//去重
if (iNums == nums[i - 1]) {
continue
}
while (l < r) {
let lNum = nums[l], rNum = nums[r], threeSum = iNums + lNum + rNum
if (threeSum < 0) {
l++
} else if (threeSum > 0) {
r--
} else {
res.push([iNums, lNum, rNum])
while (l < r && nums[l] == nums[l + 1]) {
l++
}
while (l < r && nums[r] == nums[r - 1]) {
r--
}
l++
r--
}
}
}
return res
}
18. 四数之和
建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。
思路:四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
代码实现:
var fourNum = function (nums, target) {
const len = nums.length;
if (len < 4) return [];
let res = []
nums = nums.sort((a, b) => a - b)
for (let k = 0; k < len - 3; k++) {
if (target > 0 && nums[k] > 0 && nums[k] > target) {
return res
}
if (k > 0 && nums[k] === nums[k - 1]) {
continue
}
for (let i = k + 1; i < len - 2; i++) {
if (nums[k] + nums[i] > target && nums[i] >= 0) {
break
}
if (i > k + 1 && nums[i] === nums[i - 1]) {
continue
}
let l = i + 1, r = len - 1;
while (l < r) {
let lNum = nums[l], rNum = nums[r], sum = nums[k] + nums[i] + lNum + rNum
if (sum > target) {
r--;
continue
} else if (sum < target) {
l++;
continue
} else {
res.push([nums[k], nums[i], lNum, rNum])
}
//去重
while (l < r && nums[l] === nums[++l]);
while (l < r && nums[r] === nums[--r]);
}
}
}
return res
}
总结
一般来说哈希表都是用来快速判断一个元素是否出现集合里。
对于哈希表,要知道哈希函数和哈希碰撞在哈希表中的作用.
哈希函数是把传入的key映射到符号表的索引上。
哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。
接下来是常见的三种哈希结构:
- 数组
- set(集合)
- map(映射)
只有对这些数据结构的底层实现很熟悉,才能灵活使用,否则很容易写出效率低下的程序。