今日任务
● 哈希表理论基础
● 242.有效的字母异位词
● 349. 两个数组的交集
● 202. 快乐数
● 1. 两数之和
1.哈希表理论基础
-
官方定义
- 哈希表(Hash table) 是根据关键码的值而直接进行访问的数据结构。
-
个人理解版
- 能根据索引下标直接查找到具体数值
- 哈希表就是一个数组
- 能根据索引下标直接查找到具体数值
-
哈希函数
- 函数定义
-
图片来自代码随想录官网
-
index = hashFunction(name)
-
hashFunction = hashCode(name) % tableSize
-
- 通过 hashCode() 函数将 其他数据格式转化为不同的数值,将转换后的数值映射为 哈希表 上的索引数字
- 如果 转换后的数值 大于 表长
- 会以 表长 做一次 取模操作
- 如果 转换后的数值 大于 表长
- 函数定义
-
哈希碰撞
-
图片来自代码随想录官网
-
两个数据被 hashCode() 算出来的数值一致,即映射到同一个索引下标
- 不做任何处理,这两个值会造成数据覆盖,导致数据丢失
-
解决方法
- 拉链法
-
图片来自代码随想录官网
-
同一个索引后面用链表关联无限延伸
-
注意事项
- 要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间
-
- 线性探测法
-
保证 哈希表长 一定大于 存储的数据总数
-
图片来自代码随想录官网
-
- 拉链法
-
-
常见的哈希结构
- 数组
- set(集合)
- Golang没有原生set结构,可使用第三方包或map来实现
- map(映射)
-
总结
- 当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法
- 卡哥提示
- 如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
- 哈希表采取的是空间换时间的策略,需要考量开销
2. 有效的字母异位词
关联 leetcode 242.有效的字母异位词
- 能用数组解决的优先使用数组,内存开销相对较小,运算更快
- 数据总量不大
- 能使用数组的 数字索引 解决
- 解法
-
使用数组解决
func isAnagram(s string, t string) bool { arr := [26]int{} // 全是小写字母一共26个, 给每个字母分配一个位置从0开始 // go 里面 string 的组成是 rune for _, r := range s { arr[r-rune('a')]++ } for _, r := range t { arr[r-rune('a')]-- } for _, r := range arr { if r != 0 { return false } } return true }
-
使用字典【使用库函数】
func isAnagram(s string, t string) bool { arr := make(map[rune]int) for _, i := range s { arr[i]++ } arr2 := make(map[rune]int) for _, i := range t { arr2[i]++ } return reflect.DeepEqual(arr, arr2) }
-
使用字典
func isAnagram(s string, t string) bool { arr := make(map[rune]int) for _, i := range s { arr[i]++ } for _, v := range t { arr[v]-- } for _, v := range arr { if v != 0 { return false } } return true }
-
3. 两个数组的交集
关联 leetcode 349. 两个数组的交集
-
set:集合
- 特性:
- 包含元素唯一
- 特性:
-
goalng中没有原生set,使用map来替代
func intersection(nums1 []int, nums2 []int) []int { /* 使用了Golang特性 map的逗号语法 */ rets := make([]int, 0) //只用map的key就够了, val类型随意, 避免默认零值干扰, 这里用struct{} 作为val 的类型 set := make(map[int]struct{}) for _, num := range nums1 { if _, ok := set[num]; !ok { // 集合里面不存在该元素, 添加该元素 set[num] = struct{}{} } } for _, num := range nums2 { if _, ok := set[num]; ok { // 集合里面有这个元素 rets = append(rets, num) delete(set, num) // 从集合中移除该元素 } } return rets }
4. 快乐数
关联 leetcode 202. 快乐数
-
思路:
- 使用一个集合来存储计算结果
- 如果这个计算结果在集合里面存在 —> 之前已经计算过了
- 必然进入无限循环
-
注意:
- 确定自己的循环终止条件
- 累加和的归零操作位置
- 与循环终止条件相关
-
题解
func isHappy(n int) bool { // 存储循环计算过的值 calculateSet := make(map[int]struct{}) //如果再度出现n则会导致无限循环, 后面的计算结果同理 for sum := 0; sum != 1; { sum = 0 for n > 0 { tmp := n % 10 sum += tmp * tmp n /= 10 } if _, getRepeatValue := calculateSet[sum]; getRepeatValue { return false } calculateSet[sum] = struct{}{} n = sum } return true }
5. 两数之和(哈希表解法)
-
思路:
- 使用 **哈希表【此处用map形式】**来保存遍历过的数
- 本来就要遍历一遍整数数组,则
- 只需要计算和目标的插值
- 在遍历过的哈希表中查找即可
-
题解
func twoSum(nums []int, target int) []int { loopMap := make(map[int]int) for i := range nums { sub := target - nums[i] if idx, ok := loopMap[sub]; ok { return []int{i, idx} } loopMap[nums[i]] = i } return nil }
-
总结:
- 哈希表可应对题目:
- 查找值是否出现过
- 优化循环嵌套
- 查找值是否出现过
- 哈希表可应对题目: