学习目标:
做完四道算法题,并根据视频和文章巩固哈希表理论基础
学习时间:
- 晚上七点-晚上十点
哈希表理论基础
建议:大家要了解哈希表的内部实现原理,哈希函数,哈希碰撞,以及常见哈希表的区别,数组,set 和map。
什么时候想到用哈希法,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。 这句话很重要,大家在做哈希表题目都要思考这句话。
242.有效的字母异位词
建议: 这道题目,大家可以感受到 数组 用来做哈希表 给我们带来的遍历之处
思路:数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。
操作动画如下:
代码实现:
var isAnagram = function (s, t) {
if (s.length !== t.length) {
return false
}
let record = new Array(26).fill(0)
const base = "a".charCodeAt()
for (const i of s) {
record[i.charCodeAt() - base]++
}
for (const j of t) {
if (!record[j.charCodeAt() - base]) {
return false
}
record[j.charCodeAt() - base]--
}
return true
}
349. 两个数组的交集
建议:本题就开始考虑 什么时候用set 什么时候用数组,本题其实是使用set的好题,但是后来力扣改了题目描述和 测试用例,添加了 0 <= nums1[i], nums2[i] <= 1000 条件,所以使用数组也可以了,不过建议大家忽略这个条件。 尝试去使用set。
思路:
这道题目,主要要学会使用一种哈希数据结构:unordered_set,这个数据结构可以解决很多类似的问题。注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序
这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。
那么用数组来做哈希表也是不错的选择,例如242. 有效的字母异位词(opens new window)
但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。
而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
此时就要使用另一种结构体了,set
代码实现:
var intersection = function (nums1, nums2) {
if (nums1.length < nums2.length) {
const num1 = nums1
nums1 = nums2
nums2 = num1
}
const nums1Set = new Set(nums1)
const resSet = new Set();
for (let i = 0; i < nums2.length; i++) {
nums1Set.has(nums2[i]) && resSet.add(nums2[i])
}
return Array.from(resSet)
}
202. 快乐数
思路:
所以这道题目使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
判断sum是否重复出现就可以使用unordered_set。
还有一个难点就是求和的过程,如果对取数值各个位上的单数操作不熟悉的话,做这道题也会比较艰难。
代码实现:
var isHappy1 = function (n) {
let record = new Map()
const getSum = (num) => {
let sum = 0;
while (n) {
sum += (n % 10) ** 2
n = Math.floor(n / 10)
}
return sum
}
while (true) {
if (record.has(n)) {
return false
}
if (n === 1) {
return true
}
record.set(n, 1)
n = getSum(n)
}
}
1. 两数之和
建议:本题虽然是 力扣第一题,但是还是挺难的,也是 代码随想录中 数组,set之后,使用map解决哈希问题的第一题。
思路:
首先我在强调一下 什么时候使用哈希法,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。
本题呢,我就需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。
那么我们就应该想到使用哈希法了。
因为本地,我们不仅要知道元素有没有遍历过,还有知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。
代码实现:
var twoSum = function (nums, target) {
let map = new Map()
for (let i = 0; i < nums.length; i++) {
let s = target - nums[i]
const iter = map.get(s)
if (iter !== undefined) {
return [map.get(s), i]
}
map.set(nums[i], i)
}
return {}
}
总结:
总的来说哈希表的学习难度并不高,但是基础原理和使用环境需要弄清楚,尤其是数组、set和map的使用环境
使用数组和set来做哈希法的局限。
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key
需要保存key和value的时候需要用到map
继续努力 加油!