当前位置:   article > 正文

代码随想录训练营day06|哈希表part06

代码随想录训练营day06|哈希表part06

哈希表

哈希表是根据关键码的值而直接进行访问的数据结构。通俗讲,就是根据索引直接访问结构中的数据。一般来哈希表用来判断一个元素是否出现在集合里。将数据映射到哈希表上是由哈希函数完成

哈希函数

                                                                                                                                 @橘子洲啊

 哈希函数如上图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把数据映射为哈希表上的索引数字了。如果hashcode得到的数值大于哈希表大小,对其做一个取模操作,保证得到的数值不大于tableSize。

哈希碰撞

上图中,两个数据同时被哈希函数映射至一个索引,这一现象称为哈希碰撞

原因:哈希函数的输入空间远大于其输出空间,因此存在多个不同的输入映射到相同的哈希值的可能。

一般有如下几种解决办法:

1、开放地址法
(1)线性探测

索引已经存在,则在原有索引基础上后移一位,直至不发生哈希冲突

图片来源 @ DXXME

(2)再平方探测

按顺序决定值时,如果某数据的值已经存在,则在原来值的基础上先加1的平方个单位,若仍然存在则减1的平方个单位。随之是2的平方,3的平方等等。直至不发生哈希冲突。
要注意平方不能超过容量的值
Size=16的时候,找备选的单元只能取i=1,2,3,也就是距离冲突单元1,4,9个单位的位置了。

(3)伪随机探测

按顺序决定值时,如果某数据已经存在,通过随机函数随机生成一个数,在原来值的基础上加上随机数,直至不发生哈希冲突。

2、链式地址法(HashMap的哈希冲突解决方法)

对于相同的值,使用链表进行连接。使用数组存储每一个链表。
就是hashmap的底层原理 :数组+链表 就是没有红黑树

优点:
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
(3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
(4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
缺点:
(1) 指针占用较大空间时,会造成空间浪费,若空间用于增大散列表规模进而提高开放地址法的效率。

3、再哈希法

对于冲突的哈希值再次进行哈希处理,直至没有哈希冲突。
上述哈希碰撞相关内容来源于博主@DXXME

242. 有效的字母异位词

242. 有效的字母异位词

此题比较简单,直观的感受就是计算每个字符串中字母出现的个数,然后判断两个字符串每个字母出现次数是否相同。完全符合哈希表的使用规律

  1. func isAnagram(s string, t string) bool {
  2. var s_Length map[string]int = make(map[string]int)
  3. var t_Length map[string]int = make(map[string]int)
  4. // len获取的是字符的字节长度,两种方法计算字符长度:utf8.RuneCountInString()
  5. // len([]rune(s))
  6. // s[i]取出的是字符对应的utf8编码
  7. if len([]rune(s)) == len([]rune(t)) {
  8. fmt.Printf("%d\n", len(s))
  9. for i := 0; i < len([]rune(s)); i++ {
  10. s_Length[string([]rune(s)[i])]++
  11. t_Length[string(t[i])]++
  12. }
  13. for n := 0; n < len([]rune(s)); n++ {
  14. if s_Length[string(s[n])] != t_Length[string(s[n])] {
  15. return false
  16. }
  17. }
  18. return true
  19. }
  20. return false
  21. }

此处对Go语言操作字符串进行一个小总结。

1、计算字符串的长度

go中string底层是通过byte数组实现的,使用len(str)方法实际是在计算字节长度。对于len()函数,获取的是字符的字节长度,而不是字符长度。Go中有两个方法能计算字符长度:utf8.RuneCountInString()[ ]rune()方法。前者直接计算字符长度,后者是将字符转换成字符数组,再配合len()函数使用。

2、获取字符串中的字符

直接使用索引方式访问返回的是字符的utf8编码

3、Go的字符类型

byte(uint8):代表ASCII码的一个字符

rune(int32):代表一个UTF-8字符,当处理中文等其他符合字符时,需要使用该类型

由于此处直接使用两个map类型,需要占用大量的空间,对于此题目的优化方式,直接创建一个空间大小为26的数组,记录每个字母出现的次数。下列代码来源代码随想录

  1. func isAnagram(s string, t string) bool {
  2. s_num := [26]int{}
  3. for _, n := range s {
  4. fmt.Println(n)
  5. s_num[n-rune('a')]++
  6. }
  7. for _, n := range t {
  8. s_num[n-rune('a')]--
  9. }
  10. return s_num == [26]int{} //判断其是否减为0
  11. }

349. 两个数组的交集 

349. 两个数组的交集

此题也比较简单,只需要一个数组中的出现过的数存入map中,再判断另外一个数组中的元素在map中是否存在,存在就表示是两个数组的交集。但是这题有个小坑:如果遇到重复的元素,都会存入result数组中。解决方法:1、每次匹配成功后,需要删除map中该元素。2、使用set,但是Go原生数据结构中,没有set数据结构,只有第三方包("github.com/deckarep/golang-set")有实现,后续可以研究一下这个包。

  1. func intersection(nums1 []int, nums2 []int) []int {
  2. var map1 map[int]int = make(map[int]int)
  3. // 动态数组
  4. var result []int
  5. for _, n := range nums1 {
  6. map1[n]++
  7. }
  8. for _, n := range nums2 {
  9. if _, ok := map1[n]; ok {
  10. result = append(result, n)
  11. delete(map1, n)
  12. }
  13. }
  14. return result
  15. }

202.快乐数

快乐数

emm,快乐数有点不快乐。没做过这题真的很难想到如何利用map来AC。最开始一直想着硬算,一路算下去,直到算到1结束。但是这样没有办法判断fasle的情况如何停止。后来发现,如果算到中间出现了之前出现过的值,后面继续算下去也没有意义。这下就可以用到map的特性了,把每次计算的结果存入hash,当出现重复时便返回false即可

  1. func isHappy(n int) bool {
  2. map1 := make(map[int]int)
  3. for {
  4. n = getSum(n)
  5. if n == 1 {
  6. return true
  7. }
  8. if map1[n] > 0 {
  9. return false
  10. } else {
  11. map1[n]++
  12. }
  13. }
  14. }

1. 两数之和

两数之和

这个题目还挺经典的,是使用map解决hash问题的典型案例。这个题目不是那么直接可以用map解决的问题。val存元素索引,key保存nums[i]即可。然后再判断target-nums[i]是否在map中,存在则返回两者的索引

  1. func twoSum(nums []int, target int) []int {
  2. var result []int
  3. map1 := make(map[int]int)
  4. for i := 0; i < len(nums); i++ {
  5. if map1[target-nums[i]] > 0 {
  6. result = append(result, i)
  7. result = append(result, map1[target-nums[i]]-1)
  8. } else {
  9. map1[nums[i]] = i + 1
  10. }
  11. }
  12. return result
  13. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/182840
推荐阅读
相关标签
  

闽ICP备14008679号