Go数据结构与算法-Hash表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yang731227/article/details/85228200

title: Go数据结构与算法-Hash表
tags: go,算法


介绍

哈希表是一种以 键-值(key-value) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。就像我们查字典一样,比如说我们查找字典中的某一个字,通过目录索引到这个字,然后根据目录记录的页码查找到该字的具体信息。目录中的索引就是key,查找到字的信息就是vale。

基本思想

哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。

使用哈希查找有两个步骤:

  • 使用哈希函数将被查找的键转换为数组的索引。在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突
  • 处理哈希碰撞冲突

哈希冲突

在生活中我们查询字典如果安读音来可能会查找到很多相同读音的字,多个value对应着一个key,这就是哈希冲突(也叫哈希碰撞)。冲突会给查找带来麻烦,你想想,你本来查找的是“按”,但是却找到“安”字,你又得向后翻一两页,在计算机里面也是一样道理的。但哈希冲突是无可避免的,为什么这么说呢,因为你如果要完全避开这种情况,你只能每个字典去新开一个页,然后每个字在索引里面都有对应的页码,这就可以避免冲突。但是会导致空间增大(每个字都有一页)。

既然无法避免,就只能尽量减少冲突带来的损失,而一个好的哈希函数需要有以下特点:

  • 尽量使关键字对应的记录均匀分配在哈希表里面
  • 关键字极小的变化可以引起哈希值极大的变化。

哈希冲突解决办法

1.开发定址法

如果遇到冲突的时候怎么办呢?就找hash表剩下空余的空间,找到空余的空间然后插入。就像你去商店买东西,发现东西卖光了,怎么办呢?找下一家有东西卖的商家买呗。

2.链地址法

上面所说的开发定址法的原理是遇到冲突的时候查找顺着原来哈希地址查找下一个空闲地址然后插入,但是也有一个问题就是如果空间不足,那他无法处理冲突也无法插入数据,因此需要装填因子(空间/插入数据)>=1。那有没有一种方法可以解决这种问题呢?链地址法可以,链地址法的原理时如果遇到冲突,他就会在原地址新建一个空间,然后以链表结点的形式插入到该空间

演示

代码

package hashtable

import "fmt"

// hash 表, key, v 组成
type Key interface {

}

type Value interface {

}


// 生成一个hash表
type HashTable struct {
	Items map[int] Value
}


// 给hash表中添加元素
func (ht *HashTable) Put(key Key, value Value)  {
	index := customeHash(key)
	if ht.Items == nil{
		ht.Items= make(map[int]Value)
	}
	ht.Items[index] = value
}

func customeHash(k Key) int  {
	// 自定义hash算法

	key := fmt.Sprintf("%s", k)

	h := 0
	for i:=0; i<len(key); i++{
		h = 31 * h + int(key[i])
	}
	return h
}

func (ht *HashTable) Get(key Key) Value  {
	index := customeHash(key)
	return ht.Items[index]
}

func (ht *HashTable) Remove(key Key)  {
	index := customeHash(key)
	delete(ht.Items, index)
}

func (ht *HashTable) Size() int  {
	return len(ht.Items)
}

测试代码

package hashtable

import (
	"testing"
	"fmt"
)

func populateHashTable(count int, start int) *HashTable {
	dict := HashTable{}
	for i := start; i < (start + count); i++ {
		dict.Put(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i))
	}
	return &dict
}

func TestPut(t *testing.T) {
	dict := populateHashTable(3, 0)
	if size := dict.Size(); size != 3 {
		t.Errorf("Test failed, expected 3 and got %d", size)
	}

	dict.Put("magic", "shuai") //should not add a new one, just change the existing one
	if size := dict.Size(); size != 4 {
		t.Errorf("wrong count, expected 4 and got %d", size)
	}

	dict.Put("magic1", "haha")
	if size := dict.Size(); size != 5 {
		t.Errorf("Test Failed, expected 5 and got %d", size)
	}

}

猜你喜欢

转载自blog.csdn.net/yang731227/article/details/85228200
今日推荐