❝
哈希表(Hash Table)
是一种高效的数据结构,用于实现快速的插入、删除和查找操作。哈希表通过使用哈希函数(Hash Function)
将关键字(Key)
映射到表中一个位置(或称存储槽),并在该位置存储相应的值(Value)
。❞
基本结构
哈希表
的基本结构包括以下几个部分:
- 哈希表数组:一个固定大小的数组,用于存储数据。数组的大小通常为质数,以减少潜在的哈希冲突。
- 哈希函数:一个将关键字映射到哈希表数组中某个位置的函数。哈希函数的选择对哈希表的性能至关重要。
- 存储槽:哈希表数组中的每个元素称为存储槽,用于存储键值对。
- 链表/开放寻址:当发生哈希冲突时,即多个关键字映射到同一个存储槽时,可以使用链表法或开放寻址法来解决冲突。链表法是在每个存储槽中维护一个链表,所有映射到该槽的关键字都存储在链表中。开放寻址法则是寻找空闲位置来存储冲突的元素。
操作过程
哈希表的基本操作包括插入
、删除
和查找
。以下是这些操作的详细过程:
- 「插入操作」
- 计算关键字的哈希值,根据哈希值找到对应的存储槽。
- 如果该槽为空,则直接将关键字和值插入到该槽中。
- 如果该槽已被占用,根据冲突解决策略处理。
- 「删除操作」
- 计算关键字的哈希值,根据哈希值找到对应的存储槽。
- 如果该槽为空,则表示关键字不存在,直接返回。
- 如果该槽已被占用,根据冲突解决策略查找并删除对应的关键字和值。
- 「查找操作」
- 计算关键字的哈希值,根据哈希值找到对应的存储槽。
- 如果该槽为空,则表示关键字不存在,直接返回。
- 如果该槽已被占用,根据冲突解决策略查找并返回对应的关键字和值。
代码实现
在TypeScript中实现哈希表,我们可以创建一个HashTable
类,该类包含以下功能:
初始化哈希表数组和哈希函数。 插入、删除和查找操作。 处理哈希冲突(链表法)。
class HashTable<T> {
private table: Array<{ key: string; value: T }[]>;
private size: number;
constructor(size: number = 10) {
this.size = size;
this.table = new Array(size).fill(null).map(() => []);
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i);
}
return hash % this.size;
}
public insert(key: string, value: T): void {
const index = this.hash(key);
const slot = this.table[index];
const existing = slot.find((item) => item.key === key);
if (existing) {
existing.value = value;
} else {
slot.push({ key, value });
}
}
public delete(key: string): void {
const index = this.hash(key);
const slot = this.table[index];
const existingIndex = slot.findIndex((item) => item.key === key);
if (existingIndex !== -1) {
slot.splice(existingIndex, 1);
}
}
public search(key: string): T | null {
const index = this.hash(key);
const slot = this.table[index];
const existing = slot.find((item) => item.key === key);
return existing ? existing.value : null;
}
}
// 使用示例
const hashTable = new HashTable<number>();
hashTable.insert("one", 1);
hashTable.insert("two", 2);
hashTable.insert("three", 3);
console.log(hashTable.search("one")); // 输出:1
console.log(hashTable.search("two")); // 输出:2
console.log(hashTable.search("three")); // 输出:3
hashTable.delete("two");
console.log(hashTable.search("two")); // 输出:null
我们创建了一个泛型HashTable
类,它接受一个类型参数T
,表示存储在哈希表中的值的类型。我们使用简单的哈希函数(将字符串中的字符ASCII码相加并取模)来计算键的哈希值。在插入、删除和查找操作中,我们使用链表法来解决哈希冲突
优缺点
哈希表
是一种非常高效的数据结构,但也存在一些缺点。以下是哈希表的优缺点:
✅ 快速的插入、删除和查找操作,时间复杂度接近O(1)。
✅ 空间利用率高,可以根据需要动态调整哈希表的大小。
✅ 适用于处理大量数据的场景,如数据库索引、缓存系统等。
❌ 哈希冲突可能导致性能下降,尤其是在哈希函数设计不佳或数据分布不均匀的情况下。
❌ 哈希表的大小需要预先设定,如果设定过小,可能导致较高的冲突率;如果设定过大,可能导致空间浪费。
❌ 哈希表不支持有序操作,如遍历、排序等。
应用场景
哈希表
在许多场景中都有广泛应用,以下是一些常见的应用场景:
「数据库索引」:哈希表可以用于加速数据库查询操作,提高查询性能。
「缓存系统」:哈希表可以用于实现缓存系统,如Redis,提高数据访问速度。
「编译器」:哈希表可以用于实现编译器的符号表,加速变量查找和替换操作。
「字典和集合」:哈希表可以用于实现字典和集合数据结构,提高查找、插入和删除操作的性能。
扩展和优化
为了提高哈希表
的性能,可以采取以下措施:
动态调整哈希表大小
:根据哈希表的负载因子(已占用的存储槽数与总存储槽数的比值)动态调整哈希表的大小,以保持较低的冲突率和较高的空间利用率。优化哈希函数
:设计高效的哈希函数,尽量减少冲突发生的概率。使用开放寻址法
:在哈希冲突发生时,使用开放寻址法(如线性探测、二次探测、双散列等)来解决冲突,减少链表的使用,降低空间开销。
扫码体验前端练习题小册小程序
获取更多前端资讯知识