哈希表简介

什么是哈希表?

哈希表(Hash Table)是一种非常高效的数据结构,广泛应用于需要快速插入、删除和查找操作的场景中。

哈希表是一种将键(Key)映射到值(Value)的数据结构。其核心思想是通过一个哈希函数将键转化为哈希值,然后将该哈希值映射到表中的索引位置。

这样,我们可以在常数时间内完成插入、删除和查找操作。

数组 链表 哈希表
查找元素 𝑂(𝑛) 𝑂(𝑛) 𝑂(1)
添加元素 𝑂(1) 𝑂(1) 𝑂(1)
删除元素 𝑂(𝑛) 𝑂(𝑛) 𝑂(1)

基本概念

  • 哈希函数(Hash Function):用于将键映射到哈希表中位置的函数。一个好的哈希函数能够均匀地分布输入,从而减少冲突。
  • 哈希值(Hash Value):由哈希函数生成的值,用于决定元素在哈希表中的存储位置。
  • 冲突(Collision):当两个不同的键通过哈希函数映射到同一个位置时,就会发生冲突。
  • 装载因子(Load Factor):表中元素的数量与哈希表大小之比,通常用来衡量哈希表的装满程度。

哈希函数的设计

哈希函数的设计对于哈希表的性能至关重要。一个好的哈希函数应该满足以下条件:

  1. 均匀分布:尽可能均匀地将输入分布到整个哈希表中,避免聚集。

  2. 确定性:对于相同的输入,哈希函数应始终返回相同的输出。

  3. 高效性:计算快速,适合在高性能应用中使用。

  4. 尽量避免冲突:减少不同输入映射到相同哈希值的情况。

处理冲突的方法

当两个不同的键通过哈希函数映射到同一个位置时,就会发生冲突。常见的冲突处理方法包括:

链地址法(Separate Chaining)

使用链表处理冲突,每个哈希表位置存储一个链表,所有映射到该位置的元素都存储在链表中

开放寻址法(Open Addressing)

通过在哈希表中寻找下一个可用位置来存储冲突的元素

各种编程语言采取了不同的哈希表实现策略:

  • Python 采用开放寻址。字典 dict 使用伪随机数进行探测。
  • Java 采用链式地址。自 JDK 1.8 以来,当 HashMap 内数组长度达到 64 且链表长度达到 8 时,链表会转换为红黑树以提升查找性能。
  • Go 采用链式地址。Go 规定每个桶最多存储 8 个键值对,超出容量则连接一个溢出桶;当溢出桶过多时,会执行一次特殊的等量扩容操作,以确保性能。

Demo(链地址法)

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TABLE_SIZE 20

// 哈希表节点结构
typedef struct Node
{
    char *key;
    char *value;
    struct Node *next;
} Node;

// 哈希表结构
typedef struct HashTable
{
    Node *table[TABLE_SIZE];
} HashTable;

// 创建节点
Node *create_node(const char *key, const char *value)
{
    Node *new_node = (Node *)malloc(sizeof(Node));
    new_node->key = strdup(key);
    new_node->value = strdup(value);
    new_node->next = NULL;
    return new_node;
}

// 哈希函数
unsigned int hash_function(const char *key)
{
    unsigned int hash = 0;
    while (*key)
    {
        hash = (hash << 5) + *key++;
    }
    return hash % TABLE_SIZE;
}

// 初始化哈希表
HashTable *create_table()
{
    HashTable *table = (HashTable *)malloc(sizeof(HashTable));
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        table->table[i] = NULL;
    }
    return table;
}

// 插入键值对
void insert(HashTable *table, const char *key, const char *value)
{
    unsigned int index = hash_function(key);
    Node *new_node = create_node(key, value);
    if (table->table[index] == NULL)
    {
        table->table[index] = new_node;
    }
    else
    {
        Node *current = table->table[index];
        while (current->next != NULL)
        {
            if (strcmp(current->key, key) == 0)
            {
                free(current->value);
                current->value = strdup(value);
                free(new_node->key);
                free(new_node->value);
                free(new_node);
                return;
            }
            current = current->next;
        }
        if (strcmp(current->key, key) == 0)
        {
            free(current->value);
            current->value = strdup(value);
            free(new_node->key);
            free(new_node->value);
            free(new_node);
            return;
        }
        current->next = new_node;
    }
}

// 查找键值对
char *search(HashTable *table, const char *key)
{
    unsigned int index = hash_function(key);
    Node *current = table->table[index];
    while (current != NULL)
    {
        if (strcmp(current->key, key) == 0)
        {
            return current->value;
        }
        current = current->next;
    }
    return NULL;
}

// 删除键值对
void delete(HashTable *table, const char *key)
{
    unsigned int index = hash_function(key);
    Node *current = table->table[index];
    Node *previous = NULL;
    while (current != NULL)
    {
        if (strcmp(current->key, key) == 0)
        {
            if (previous == NULL)
            {
                table->table[index] = current->next;
            }
            else
            {
                previous->next = current->next;
            }
            free(current->key);
            free(current->value);
            free(current);
            return;
        }
        previous = current;
        current = current->next;
    }
}

// 释放哈希表
void free_table(HashTable *table)
{
    for (int i = 0; i < TABLE_SIZE; i++)
    {
        Node *current = table->table[i];
        while (current != NULL)
        {
            Node *temp = current;
            current = current->next;
            free(temp->key);
            free(temp->value);
            free(temp);
        }
    }
    free(table);
}

// 测试哈希表
int main()
{
    HashTable *table = create_table();

    insert(table, "key1", "value1");
    insert(table, "key2", "value2");
    insert(table, "key3", "value3");

    printf("key1: %s\n", search(table, "key1"));
    printf("key2: %s\n", search(table, "key2"));
    printf("key3: %s\n", search(table, "key3"));

    delete (table, "key2");
    printf("key2: %s\n", search(table, "key2"));

    free_table(table);
    return 0;
}
相关推荐
long31630 分钟前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
熊文豪39 分钟前
探索CANN ops-nn:高性能哈希算子技术解读
算法·哈希算法·cann
张张努力变强3 小时前
C++ STL string 类:常用接口 + auto + 范围 for全攻略,字符串操作效率拉满
开发语言·数据结构·c++·算法·stl
wWYy.3 小时前
数组快排 链表归并
数据结构·链表
李斯啦果3 小时前
【PTA】L1-019 谁先倒
数据结构·算法
Mr Xu_19 小时前
告别硬编码:前端项目中配置驱动的实战优化指南
前端·javascript·数据结构
czxyvX19 小时前
017-AVL树(C++实现)
开发语言·数据结构·c++
数智工坊19 小时前
【数据结构-队列】3.2 队列的顺序-链式实现-双端队列
数据结构
elseif12320 小时前
【C++】并查集&家谱树
开发语言·数据结构·c++·算法·图论
徐小夕@趣谈前端20 小时前
Web文档的“Office时刻“:jitword共建版2.0发布!让浏览器变成本地生产力
前端·数据结构·vue.js·算法·开源·编辑器·es6