【数据结构篇】第4期 哈希表

21 阅读5分钟

哈希表(Hash Table)是一种高效的数据结构,用于实现快速的插入、删除和查找操作。哈希表通过使用哈希函数(Hash Function)关键字(Key)映射到表中一个位置(或称存储槽),并在该位置存储相应的值(Value)

基本结构

哈希表的基本结构包括以下几个部分:

  1. 哈希表数组:一个固定大小的数组,用于存储数据。数组的大小通常为质数,以减少潜在的哈希冲突。
  2. 哈希函数:一个将关键字映射到哈希表数组中某个位置的函数。哈希函数的选择对哈希表的性能至关重要。
  3. 存储槽:哈希表数组中的每个元素称为存储槽,用于存储键值对。
  4. 链表/开放寻址:当发生哈希冲突时,即多个关键字映射到同一个存储槽时,可以使用链表法或开放寻址法来解决冲突。链表法是在每个存储槽中维护一个链表,所有映射到该槽的关键字都存储在链表中。开放寻址法则是寻找空闲位置来存储冲突的元素。

操作过程

哈希表的基本操作包括插入删除查找。以下是这些操作的详细过程:

  1. 「插入操作」
  • 计算关键字的哈希值,根据哈希值找到对应的存储槽。
  • 如果该槽为空,则直接将关键字和值插入到该槽中。
  • 如果该槽已被占用,根据冲突解决策略处理。
  1. 「删除操作」
  • 计算关键字的哈希值,根据哈希值找到对应的存储槽。
  • 如果该槽为空,则表示关键字不存在,直接返回。
  • 如果该槽已被占用,根据冲突解决策略查找并删除对应的关键字和值。
  1. 「查找操作」
  • 计算关键字的哈希值,根据哈希值找到对应的存储槽。
  • 如果该槽为空,则表示关键字不存在,直接返回。
  • 如果该槽已被占用,根据冲突解决策略查找并返回对应的关键字和值。

代码实现

在TypeScript中实现哈希表,我们可以创建一个HashTable类,该类包含以下功能:

初始化哈希表数组和哈希函数。 插入、删除和查找操作。 处理哈希冲突(链表法)。

class HashTable<T> {
  private tableArray<{ keystringvalue: T }[]>;
  private sizenumber;

  constructor(size: number = 10) {
    this.size = size;
    this.table = new Array(size).fill(null).map(() => []);
  }

  private hash(keystring): number {
    let hash = 0;
    for (let i = 0; i < key.length; i++) {
      hash += key.charCodeAt(i);
    }
    return hash % this.size;
  }

  public insert(keystringvalue: 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(keystring): 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(keystring): 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,提高数据访问速度。

「编译器」:哈希表可以用于实现编译器的符号表,加速变量查找和替换操作。

「字典和集合」:哈希表可以用于实现字典和集合数据结构,提高查找、插入和删除操作的性能。

扩展和优化

为了提高哈希表的性能,可以采取以下措施:

  1. 动态调整哈希表大小:根据哈希表的负载因子(已占用的存储槽数与总存储槽数的比值)动态调整哈希表的大小,以保持较低的冲突率和较高的空间利用率。
  2. 优化哈希函数:设计高效的哈希函数,尽量减少冲突发生的概率。
  3. 使用开放寻址法:在哈希冲突发生时,使用开放寻址法(如线性探测、二次探测、双散列等)来解决冲突,减少链表的使用,降低空间开销。

gh_3c4c8b84e5c9_430 (1).jpg

扫码体验前端练习题小册小程序

获取更多前端资讯知识