golang 散列函数 哈希Table

散列表

  • 根据键(Key)直接访问在内存储存位置的数据结构。它通过计算出一个键值的函数,将key映射到表中一个位置来访问,这加快了查找速度。 这个映射函数称做散列函数,存放记录的数组称做散列表。

  • img

  • 存储: 哈希表 使用 哈希函数 将键(key)转换成一个 哈希值。然后在数组中获取对应的值,如果有值就会用链表的形式存储。
    查询: 存储的过程理解了,查询看着下面流程也应该能理解。
    key->f(key)->index->list[index]->linked_list。
    删除: 需要注意的是删除链表的第一条数据和,链表中的数据需要额外的逻辑判断。

  • package main
    
    import (
    	"fmt"
    )
    
    type Bucket struct {
    	HKey string
    	Data string
    }
    
    type NoLinkHashTable struct {
    	Capacity int32
    	Used     int32
    	Buckets  []*Bucket
    	lock     int32
    }
    
    func NewNoLinkHashTable(length int32) *NoLinkHashTable {
    	return &NoLinkHashTable{
    		Capacity: length,
    		Buckets:  make([]*Bucket, length),
    	}
    }
    
    /**
    设置节点
    */
    func (t *NoLinkHashTable) hSet(key string, value string) {
    	bucket := &Bucket{key, value}
    	hashCode := t.getHashCode(key)
    	index := hashCode % (t.Capacity - 1)
    	t.addBucket(index, key, bucket)
    }
    
    /**
    查找节点
    */
    func (t *NoLinkHashTable) hGet(key string) *Bucket {
    	hashCode := t.getHashCode(key)
    	index := hashCode % (t.Capacity - 1)
    	return t.findBucket(index, key)
    }
    
    /**
    查找节点
    */
    func (t *NoLinkHashTable) findBucket(index int32, key string) *Bucket {
    	if ok := t.Buckets[index]; ok != nil {
    		if t.Buckets[index].HKey == key {
    			return t.Buckets[index]
    		} else {
    			index++
    		}
    		for {
    			if ok := t.Buckets[index]; ok != nil {
    				if t.Buckets[index].HKey == key {
    					return t.Buckets[index]
    				} else {
    					index++
    				}
    			} else {
    				return nil
    			}
    		}
    	}
    	return nil
    }
    
    /**
    增加节点
    */
    func (t *NoLinkHashTable) addBucket(index int32, key string, bucket *Bucket) {
    	if t.Capacity == t.Used {
    		panic("table is full")
    	}
    	fmt.Println("index:", index)
    	if ok := t.Buckets[index]; ok != nil { //key已存在
    		if ok.HKey == key { //如果key相同 直接覆盖
    			t.Buckets[index] = bucket
    			return
    		}
    		index++
    		for {
    			if index >= t.Capacity {
    				t.Capacity++
    				tmp := t.Buckets
    				t.Buckets = make([]*Bucket, 0)
    				t.Buckets = append(t.Buckets, tmp...)
    				index = 0
    			}
    			if ok := t.Buckets[index]; ok != nil {
    				if ok.HKey == key { //如果key相同 直接覆盖
    					t.Buckets[index] = bucket
    					return
    				}
    				index++
    			}
    		}
    	} else {
    		t.Buckets[index] = bucket
    		t.Used++
    	}
    
    }
    
    func (t *NoLinkHashTable) getHashCode(key string) int32 {
    	sum := 0
    	for _, v := range key {
    		sum += int(v)
    	}
    	return int32(sum) % t.Capacity
    }
    
    func main() {
    	b := NewNoLinkHashTable(8)
    	b.hSet("name", "xiaoGe")
    	b.hSet("sex", "男")
    	b.hSet("age", "30")
    	fmt.Println(b.hGet("name").Data)
    	fmt.Println(b.hGet("sex").Data)
    	/**
    	  index: 1
    	  index: 0
    	  index: 5
    	  xiaoGe
    	  男
    
    	*/
    }
    
    
### 哈希表的底层实现方式 哈希表是一种通过键值映射来存储和检索数据的数据结构。其核心目标是以接近 O(1) 的时间复杂度完成插入、删除和查找操作。以下是关于哈希表底层实现的具体分析: #### 一、基本组成 哈希表通常由两部分构成:**数组** 和 **链表(或其他解决冲突的方式)**。 - 数组作为主要容器,用于存储经过哈希计算后的索引位置。 - 链表则被用来处理因哈希碰撞而产生的多个键值对的情况。 在 Golang 中,哈希表的底层数据结构是由数组加单链表组成的[^1]。具体来说,每个链表节点包含了八个 `key`、`value` 和键的高八位信息。 #### 二、哈希函数的作用 哈希函数负责将输入的关键字转换成对应的数组下标。一个好的哈希函数应具备以下几个特点: - 尽量均匀分布散列结果; - 计算速度快; - 能够有效减少冲突概率。 对于非整型 Key 或者当数据范围较大时,可以通过特定方法如直接地址法或者取模运算等方式构建合适的哈希函数[^3]。 #### 三、冲突解决方案 即使有了优秀的哈希函数,仍然难以完全避免不同关键字产生相同哈希值的现象——即所谓的“哈希冲突”。针对这一问题常见的策略有以下几种: 1. **拉链法 (Separate Chaining)** 这是最常用的手段之一,在每个桶里维护一条链表,所有具有相同 hash code 的元素都挂载在这条链上。例如 Redis 使用的就是这种模式,并定义了一个名为 `dictEntry` 的结构体表示每一个节点[^4]: ```c typedef struct dictEntry { void *key; union{ void *val; uint64_t u64; int64_t s64; }; struct dictEntry *next; } dictEntry; ``` 2. **开放寻址法 (Open Addressing)** 当某个槽位已被占用时,则按照一定规则寻找其他可用空间存放新项。此方法不需要额外开辟内存区域保存溢出项目,但可能会增加访问成本并降低性能。 #### 四、实际应用中的优化措施 随着技术发展以及应用场景需求的变化,现代编程语言往往会对传统理论模型做出适当调整以适应实际情况。比如 Go 在设计自己的 map 类型时就引入了一些独特的机制来提升效率;同样地,Redis 对内部使用的哈希表也进行了诸多改进使其更适合缓存场景下的高频读写操作。 综上所述,无论是哪种具体的实现形式,它们背后的核心思想都是围绕着如何高效利用有限资源达成预期功能展开讨论的。 ```python # Python 示例代码展示简单的哈希表概念 class HashTable: def __init__(self, size=10): self.size = size self.table = [[] for _ in range(self.size)] def _hash_function(self, key): return sum(ord(c) for c in str(key)) % self.size def insert(self, key, value): index = self._hash_function(key) bucket = self.table[index] found_key = False for i, kv in enumerate(bucket): k, v = kv if key == k: bucket[i] = (key, value) found_key = True if not found_key: bucket.append((key, value)) def get(self, key): index = self._hash_function(key) bucket = self.table[index] for k, v in bucket: if key == k: return v raise KeyError(f"{key} does not exist.") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小哥(xpc)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值