哈希表完全解析:从算法原理到C语言实战,一次彻底搞懂Hash!
内容较多,但非常全面,内容涵盖了哈希表从算法原理 → 代码实现 → 实际应用 → 面试考点的全流程,非常适合做学习资料或面试复盘。

本文将带你从零开始,深入理解哈希算法的原理与实现,掌握链地址法、开放寻址法、负载控制与扩容机制,并通过完整C语言示例代码构建自己的哈希库。
无论你是准备面试、写底层代码,还是做嵌入式内存优化,这都是一篇你读完就能上手实战的 Hash 全指南。


目录

一、哈希(Hash)算法

二、哈希表(Hash Table)

三、基本思想(原理)

四、哈希函数算法

五、解决冲突

六、C语言简单实现

1 简单示例(链地址法)

2 C语言库(完整版)

七、面试常见问题


一、哈希(Hash)算法

哈希算法(Hash Function)的作用是:

把“任意长度的输入数据”转换成“固定长度的整数值”。

这个整数值称为哈希值(Hash Value)散列值
哈希算法的核心目标是:

  • 快速查找(O(1) 平均时间)

  • 快速插入 / 删除

  • 避免冲突(Collision)


二、哈希表(Hash Table)

哈希表是一种根据键(key)快速访问值(value)的数据结构。
它的实现原理是:

  1. 使用哈希函数计算出 key 的哈希值;

  2. 把哈希值对数组长度取模,得到存放位置;

  3. 将数据存入这个位置。

例如:

index = hash("apple") % table_size;
table[index] = "red";

这样,下次查找 "apple" 时,只需要一次哈希计算,就能直接定位。


三、基本思想(原理)

哈希表主要由三部分组成:

  1. 哈希函数 (Hash Function)
    把“键”变成一个数字(索引)。
    比如字符串 "apple" → 计算得到哈希值 1234567
    再用 % 表的大小 → 比如 % 127 = 5,那么就放进第 5 号桶。

  2. 桶(Bucket)数组
    整个哈希表其实就是一堆“桶”。
    每个桶里装若干 key-value 对。
    如果两个 key 算出来的哈希值一样(叫哈希冲突),它们就会放在同一个桶里。

  3. 解决冲突
    当两个键算出来的位置一样时,怎么办?
    常见两种方法:

    • 链表法(拉链法):每个桶里放一个链表。

    • 开放寻址法:找下一个空位。


 四、哈希函数算法

哈希函数是可以自定义、根据不同数据类型或场景变化的
它的核心目标只有一个:

把任意键值(key)尽量均匀地映射到一个有限范围内的整数(索引)。

所以具体算法取决于:

  • 你要哈希的键类型(数字 / 字符串 / 结构体等)

  • 你要追求的速度 vs 分布均匀性

  • 你表的大小和应用场景

1️⃣ 数值型 Key

如果 key 是整数:

hash = key % table_size;

简单直接,但可能分布不均。
改进方式:

  • 使用乘法法(Knuth 推荐)

    hash = (int)(table_size * (key * A - floor(key * A)));  // A为常数≈0.618
    

2️⃣ 字符串型 Key(最常见)

对于字符串(如 "apple"),常见几种方法:

(1)加权求和法(简单)

unsigned int hash = 0;
for (char *p = str; *p; p++) {
    hash += *p;  // 每个字符的ASCII累加
}
hash %= table_size;

👉 简单但容易冲突。

(2)乘法 + 累加法(经典)

unsigned int hash = 0;
for (char *p = str; *p; p++) {
    hash = 31 * hash + *p;  // 31可以换成131, 1313等质数
}
return hash % table_size;

✅ 这是 Java 的 String.hashCode() 原理。
它计算快、分布好。

(3)BKDR Hash(常用)

unsigned int BKDRHash(char *str) {
    unsigned int seed = 131; // 或 31, 1313, 13131 等
    unsigned int hash = 0;
    while (*str)
        hash = hash * seed + (*str++);
    return (hash & 0x7FFFFFFF);
}

✅ 常用于 C/C++ 程序,简单高效。

(4)DJB2 Hash(经典稳定)

unsigned int DJBHash(char *str) {
    unsigned int hash = 5381;
    while (*str)
        hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
    return hash;
}

✅ 分布好、实现简单,是 Linux/Redis 中常见算法。


3️⃣ 结构体 Key(多字段组合)

可以把多个字段的哈希混合:

hash = (hash_int(id) * 31 + hash_str(name)) % table_size;

或使用通用的“混合函数”(bit-mix)。


4️⃣ 工业级高性能哈希算法

在性能要求高的系统中(如数据库、哈希索引、语言运行时),
常用更复杂的散列算法,如:

算法名 特点 常见应用
MurmurHash 高速、均匀、非加密 Redis、LevelDB
CityHash / FarmHash Google 出品,针对长字符串优化 Bigtable、TensorFlow
FNV-1 / FNV-1a 简单且效果好 HashMap, 文件校验
CRC32 / SHA-1 等 校验或安全性场景 Git、网络传输

🧩 补充:选择哈希函数时的考虑

目标 推荐算法
简单、教学用 乘法 + 累加法、DJB2
实际项目、性能优先 MurmurHash、FNV
安全要求高 SHA-256、MD5(不推荐用于安全)
小型嵌入式系统 BKDR、DJB2(运算轻)

五、解决冲突

 🔄 1 链地址法(Separate Chaining,也叫拉链法)

💡 基本思想

每个桶(bucket)不只放一个元素,而是放一个**链表(或其他结构)**来存放所有哈希到同一个桶的键值对。

当发生冲突时,不找别的位置,而是在同一个桶的链表尾部插入


📦 结构示意

哈希表(数组):
[0] → NULL
[1] → ("apple", 5) → ("cat", 7) → NULL
[2] → ("banana", 8) → NULL
[3] → NULL
[4] → ("pear", 9) → NULL

比如:

  • “apple” → 哈希值 = 1

  • “cat” → 哈希值 = 1(冲突!)
    → 插到 [1] 的链表后面。


🧩 C语言示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TABLE_SIZE 5

typedef struct Node {
    char *key;
    int value;
    struct Node *next;
} Node;

Node *hashTable[TABLE_SIZE] = {NULL};

unsigned int hash(char *key) {
    unsigned int hash = 0;
    while (*key)
        hash = (hash * 31) + (*key++);
    return hash % TABLE_SIZE;
}

void insert(char *key, int value) {
    unsigned int idx = hash(key);
    Node *newNode = malloc(sizeof(Node));
    newNode->key = strdup(key);
    newNode->value = value;
    newNode->next = hashTable[idx];
    hashTable[idx] = newNode;
}

int search(char *key) {
    unsigned int idx = hash(key);
    Node *p = hashTable[idx];
    while (p) {
        if (strcmp(p->key, key) == 0)
            return p->value;
        p = p->next;
    }
    return -1;
}

优点:

  • 实现简单。

  • 表容量满了也能插入(只需链表加长)。

  • 删除元素方便(直接操作链表)。

⚠️ 缺点:

  • 需要额外的内存存储链表节点。

  • 当冲突多时(链太长),查找效率降低。


🔄 2 开放寻址法(Open Addressing)

💡 基本思想

如果一个位置已经被占,就按照某种探查规则(探测序列)
继续寻找下一个空位直到找到为止。

所有元素都存在数组中,不使用链表。


📦 结构示意

假设表长 = 7,哈希函数为 hash(key) = key % 7

插入序列:14,21,28,35

14 → 0号位置
21 → 0冲突 → 1号位置
28 → 0冲突 → 1冲突 → 2号位置
35 → 0冲突 → 1冲突 → 2冲突 → 3号位置

🚀 探查方式(重点)

开放寻址法的区别主要在“探查序列”:

类型 规则 特点
线性探查 (Linear Probing) i, i+1, i+2... 简单但容易“聚集”
二次探查 (Quadratic Probing) i, i+1², i+2²... 减少聚集
双重哈希 (Double Hashing) i, i+h2(key) 分布更随机

🧩 线性探查 C语言示例

#include <stdio.h>
#include <string.h>

#define TABLE_SIZE 7

typedef struct {
    int used;
    char *key;
    int value;
} Entry;

Entry table[TABLE_SIZE] = {0};

unsigned int hash(char *key) {
    unsigned int h = 0;
    while (*key)
        h = (h * 31) + (*key++);
    return h % TABLE_SIZE;
}

void insert(char *key, int value) {
    unsigned int idx = hash(key);
    while (table[idx].used) {
        idx = (idx + 1) % TABLE_SIZE;  // 线性探查
    }
    table[idx].key = key;
    table[idx].value = value;
    table[idx].used = 1;
}

int search(char *key) {
    unsigned int idx = hash(key);
    int start = idx;
    while (table[idx].used) {
        if (strcmp(table[idx].key, key) == 0)
            return table[idx].value;
        idx = (idx + 1) % TABLE_SIZE;
        if (idx == start) break;  // 避免死循环
    }
    return -1;
}

优点:

  • 数据全在一块连续内存中(Cache 友好)。

  • 不需要链表或指针,结构简单。

⚠️ 缺点:

  • 表容易“变满”。

  • 删除复杂(要标记“删除”而不能直接清空)。

  • 当负载因子(装填率)高时性能迅速下降。


📊 两种方法对比总结

项目 链地址法 开放寻址法
存储结构 数组 + 链表 单一数组
空间利用率 较低(需指针) 较高
查找效率 冲突多时退化为链表 冲突多时退化为线性扫描
删除操作 简单 复杂(需特殊标记)
适合场景 动态插入删除多 数据较密、空间紧凑场景

链地址法: 冲突元素挂在链表上。
开放寻址法: 冲突元素找下一个空位。

六、C语言简单实现

1 简单示例(链地址法)

下面是一个简化的哈希表实现(key 为字符串,value 为整数)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TABLE_SIZE 10

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

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

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

// 哈希函数(简单字符串哈希)
unsigned int hash(const char *key) {
    unsigned int hash = 0;
    while (*key) {
        hash = hash * 31 + *key++;
    }
    return hash % TABLE_SIZE;
}

// 插入键值对
void insert(HashTable *table, const char *key, int value) {
    unsigned int index = hash(key);
    Node *newNode = malloc(sizeof(Node));
    newNode->key = strdup(key);
    newNode->value = value;
    newNode->next = table->buckets[index];
    table->buckets[index] = newNode;
}

// 查找值
int* search(HashTable *table, const char *key) {
    unsigned int index = hash(key);
    Node *curr = table->buckets[index];
    while (curr) {
        if (strcmp(curr->key, key) == 0)
            return &curr->value;
        curr = curr->next;
    }
    return NULL;
}

// 删除键
void delete(HashTable *table, const char *key) {
    unsigned int index = hash(key);
    Node *curr = table->buckets[index];
    Node *prev = NULL;

    while (curr) {
        if (strcmp(curr->key, key) == 0) {
            if (prev) prev->next = curr->next;
            else table->buckets[index] = curr->next;
            free(curr->key);
            free(curr);
            return;
        }
        prev = curr;
        curr = curr->next;
    }
}

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

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

    insert(table, "apple", 10);
    insert(table, "banana", 20);
    insert(table, "orange", 30);

    int *val = search(table, "banana");
    if (val)
        printf("banana => %d\n", *val);
    else
        printf("Not found\n");

    delete(table, "banana");

    val = search(table, "banana");
    if (val)
        printf("banana => %d\n", *val);
    else
        printf("Not found\n");

    free_table(table);
    return 0;
}

运行结果示例

banana => 20
Not found

2 C语言库(完整版)

hash.h

#ifndef HASHLIB_H

#define HASHLIB_H

#include <stddef.h>

#ifdef __cplusplus

extern "C" {

#endif

/* 哈希表键类型 */

typedef enum {

    HASH_KEY_INT = 0,      /* 整型键 */

    HASH_KEY_STR = 1,      /* 字符串键 */

    HASH_KEY_PTR = 2       /* 指针键 */

} hash_key_type_t;

/* 哈希算法类型 */

typedef enum {

    HASH_ALGO_DJB2 = 0,    /* DJB2算法 */

    HASH_ALGO_BKDR = 1,    /* BKDR算法 */

    HASH_ALGO_SDBM = 2,    /* SDBM算法 */

    HASH_ALGO_RS = 3       /* RS算法 */

} hash_algo_t;

/* 冲突解决方法 */

typedef enum {

    COLLISION_CHAINING = 0, /* 链地址法 */

    COLLISION_LINEAR = 1,   /* 线性探测 */

    COLLISION_QUADRATIC = 2 /* 二次探测 */

} collision_method_t;

/* 哈希表句柄 */

typedef struct hash_table hash_table_t;

/* 创建哈希表

 * initial_size: 初始大小,0表示使用默认值

 * key_type: 键类型

 * algo: 哈希算法

 * method: 冲突解决方法

 */

hash_table_t* hash_table_create(size_t initial_size,

                                hash_key_type_t key_type,

                                hash_algo_t algo,

                                collision_method_t method);

/* 销毁哈希表 */

void hash_table_destroy(hash_table_t* ht);

/* 插入键值对 */

int hash_table_put(hash_table_t* ht, const void* key, void* value);

/* 获取值 */

void* hash_table_get(hash_table_t* ht, const void* key);

/* 删除键值对 */

int hash_table_remove(hash_table_t* ht, const void* key);

/* 获取元素数量 */

size_t hash_table_size(hash_table_t* ht);

/* 判断是否为空 */

int hash_table_is_empty(hash_table_t* ht);

/* 清空哈希表 */

void hash_table_clear(hash_table_t* ht);

/* 遍历哈希表 */

int hash_table_foreach(hash_table_t* ht,

                       int (*callback)(const void* key, void* value, void* user_data),

                       void* user_data);

/* 获取负载因子 */

double hash_table_load_factor(hash_table_t* ht);

/* 重新哈希(扩容) */

int hash_table_rehash(hash_table_t* ht, size_t new_size);

/* 字符串哈希算法函数(可直接使用) */

unsigned long hash_djb2(const char* str);

unsigned long hash_bkdr(const char* str);

unsigned long hash_sdbm(const char* str);

unsigned long hash_rs(const char* str);

#ifdef __cplusplus

}

#endif

#endif /* HASHLIB_H */

hash.cpp

#include "hashlib.h"

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

#include <assert.h>

/* 默认质数表,用于哈希表大小 */

static const size_t PRIMES[] = {

    53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,

    49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469

};

#define PRIME_COUNT (sizeof(PRIMES) / sizeof(PRIMES[0]))

#define DEFAULT_LOAD_FACTOR 0.75

#define MAX_LOAD_FACTOR 0.85

/* 哈希表条目状态 */

typedef enum {

    ENTRY_EMPTY = 0,

    ENTRY_OCCUPIED = 1,

    ENTRY_DELETED = 2

} entry_status_t;

/* 哈希表条目 */

typedef struct hash_entry {

    void* key;              /* 键 */

    void* value;            /* 值 */

    entry_status_t status;  /* 状态 */

    struct hash_entry* next; /* 链地址法中的下一个条目 */

} hash_entry_t;

/* 哈希表结构 */

struct hash_table {

    hash_entry_t* entries;      /* 条目数组 */

    size_t capacity;           /* 容量 */

    size_t size;               /* 当前大小 */

    hash_key_type_t key_type;  /* 键类型 */

    hash_algo_t algorithm;     /* 哈希算法 */

    collision_method_t method; /* 冲突解决方法 */

    double max_load_factor;    /* 最大负载因子 */

};

/* 选择最接近的质数 */

static size_t find_nearest_prime(size_t min_size) {

    for (size_t i = 0; i < PRIME_COUNT; i++) {

        if (PRIMES[i] >= min_size) {

            return PRIMES[i];

        }

    }

    return PRIMES[PRIME_COUNT - 1];

}

/* 字符串哈希算法 */

/* DJB2算法 */

unsigned long hash_djb2(const char* str) {

    unsigned long hash = 5381;

    int c;

    while ((c = *str++)) {

        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    }

    return hash;

}

/* BKDR算法 */

unsigned long hash_bkdr(const char* str) {

    unsigned long hash = 0;

    unsigned long seed = 131; /* 31, 131, 1313, 13131, 131313 etc. */

    int c;

    while ((c = *str++)) {

        hash = hash * seed + c;

    }

    return hash;

}

/* SDBM算法 */

unsigned long hash_sdbm(const char* str) {

    unsigned long hash = 0;

    int c;

    while ((c = *str++)) {

        hash = c + (hash << 6) + (hash << 16) - hash;

    }

    return hash;

}

/* RS算法 */

unsigned long hash_rs(const char* str) {

    unsigned long hash = 0;

    unsigned long a = 63689;

    unsigned long b = 378551;

    int c;

    while ((c = *str++)) {

        hash = hash * a + c;

        a = a * b;

    }

    return hash;

}

/* 根据算法类型计算哈希值 */

static unsigned long compute_hash(hash_table_t* ht, const void* key) {

    if (ht->key_type == HASH_KEY_STR) {

        const char* str = (const char*)key;

        switch (ht->algorithm) {

            case HASH_ALGO_DJB2: return hash_djb2(str);

            case HASH_ALGO_BKDR: return hash_bkdr(str);

            case HASH_ALGO_SDBM: return hash_sdbm(str);

            case HASH_ALGO_RS: return hash_rs(str);

            default: return hash_djb2(str);

        }

    } else if (ht->key_type == HASH_KEY_INT) {

        int int_key = *(const int*)key;

        /* 使用Knuth乘法哈希 */

        return (unsigned long)(int_key * 2654435761u);

    } else { /* HASH_KEY_PTR */

        /* 直接使用指针地址 */

        return (unsigned long)key;

    }

}

/* 比较键 */

static int keys_equal(hash_table_t* ht, const void* key1, const void* key2) {

    if (ht->key_type == HASH_KEY_STR) {

        return strcmp((const char*)key1, (const char*)key2) == 0;

    } else if (ht->key_type == HASH_KEY_INT) {

        return *(const int*)key1 == *(const int*)key2;

    } else { /* HASH_KEY_PTR */

        return key1 == key2;

    }

}

/* 复制键 */

static void* copy_key(hash_table_t* ht, const void* key) {

    if (ht->key_type == HASH_KEY_STR) {

        return strdup((const char*)key);

    } else if (ht->key_type == HASH_KEY_INT) {

        int* new_key = malloc(sizeof(int));

        *new_key = *(const int*)key;

        return new_key;

    } else { /* HASH_KEY_PTR */

        return (void*)key; /* 指针不需要复制 */

    }

}

/* 释放键 */

static void free_key(hash_table_t* ht, void* key) {

    if (ht->key_type == HASH_KEY_STR) {

        free(key);

    } else if (ht->key_type == HASH_KEY_INT) {

        free(key);

    }

    /* 指针键不需要释放 */

}

/* 计算索引 */

static size_t compute_index(hash_table_t* ht, unsigned long hash) {

    return hash % ht->capacity;

}

/* 链地址法:查找条目 */

static hash_entry_t* chaining_find_entry(hash_table_t* ht, size_t index, const void* key) {

    hash_entry_t* entry = &ht->entries[index];

   

    /* 检查第一个条目 */

    if (entry->status == ENTRY_OCCUPIED && keys_equal(ht, entry->key, key)) {

        return entry;

    }

   

    /* 检查链表中的其他条目 */

    entry = entry->next;

    while (entry != NULL) {

        if (keys_equal(ht, entry->key, key)) {

            return entry;

        }

        entry = entry->next;

    }

   

    return NULL;

}

/* 链地址法:插入条目 */

static int chaining_insert_entry(hash_table_t* ht, size_t index, void* key, void* value) {

    hash_entry_t* entry = &ht->entries[index];

   

    /* 如果第一个条目为空,直接使用 */

    if (entry->status != ENTRY_OCCUPIED) {

        entry->key = key;

        entry->value = value;

        entry->status = ENTRY_OCCUPIED;

        entry->next = NULL;

        ht->size++;

        return 1;

    }

   

    /* 检查是否已存在相同键 */

    if (keys_equal(ht, entry->key, key)) {

        entry->value = value;

        free_key(ht, key); /* 释放复制的键 */

        return 0;

    }

   

    /* 查找链表末尾 */

    hash_entry_t* prev = entry;

    while (prev->next != NULL) {

        if (keys_equal(ht, prev->next->key, key)) {

            prev->next->value = value;

            free_key(ht, key);

            return 0;

        }

        prev = prev->next;

    }

   

    /* 创建新条目并添加到链表末尾 */

    hash_entry_t* new_entry = malloc(sizeof(hash_entry_t));

    if (new_entry == NULL) {

        return -1;

    }

    new_entry->key = key;

    new_entry->value = value;

    new_entry->status = ENTRY_OCCUPIED;

    new_entry->next = NULL;

    prev->next = new_entry;

    ht->size++;

    return 1;

}

/* 开放地址法:查找下一个索引 */

static size_t open_address_next_index(hash_table_t* ht, size_t index, int probe) {

    if (ht->method == COLLISION_LINEAR) {

        return (index + probe) % ht->capacity;

    } else { /* COLLISION_QUADRATIC */

        return (index + probe * probe) % ht->capacity;

    }

}

/* 开放地址法:查找条目 */

static hash_entry_t* open_address_find_entry(hash_table_t* ht, size_t index, const void* key) {

    int probe = 0;

    size_t current_index = index;

   

    while (ht->entries[current_index].status != ENTRY_EMPTY) {

        if (ht->entries[current_index].status == ENTRY_OCCUPIED &&

            keys_equal(ht, ht->entries[current_index].key, key)) {

            return &ht->entries[current_index];

        }

       

        probe++;

        current_index = open_address_next_index(ht, index, probe);

       

        /* 防止无限循环 */

        if (probe > ht->capacity) {

            break;

        }

    }

   

    return NULL;

}

/* 开放地址法:插入条目 */

static int open_address_insert_entry(hash_table_t* ht, size_t index, void* key, void* value) {

    int probe = 0;

    size_t current_index = index;

   

    while (ht->entries[current_index].status == ENTRY_OCCUPIED) {

        /* 检查是否已存在相同键 */

        if (keys_equal(ht, ht->entries[current_index].key, key)) {

            ht->entries[current_index].value = value;

            free_key(ht, key); /* 释放复制的键 */

            return 0;

        }

       

        probe++;

        current_index = open_address_next_index(ht, index, probe);

       

        /* 防止无限循环 */

        if (probe > ht->capacity) {

            return -1;

        }

    }

   

    /* 找到空位或删除位 */

    ht->entries[current_index].key = key;

    ht->entries[current_index].value = value;

    ht->entries[current_index].status = ENTRY_OCCUPIED;

    ht->entries[current_index].next = NULL;

    ht->size++;

    return 1;

}

/* 初始化哈希表条目 */

static void init_entries(hash_entry_t* entries, size_t count) {

    for (size_t i = 0; i < count; i++) {

        entries[i].key = NULL;

        entries[i].value = NULL;

        entries[i].status = ENTRY_EMPTY;

        entries[i].next = NULL;

    }

}

/* 释放链地址法中的链表 */

static void free_chaining_list(hash_entry_t* entry) {

    hash_entry_t* current = entry->next;

    while (current != NULL) {

        hash_entry_t* next = current->next;

        free(current);

        current = next;

    }

    entry->next = NULL;

}

/* 创建哈希表 */

hash_table_t* hash_table_create(size_t initial_size,

                                hash_key_type_t key_type,

                                hash_algo_t algo,

                                collision_method_t method) {

    hash_table_t* ht = malloc(sizeof(hash_table_t));

    if (ht == NULL) {

        return NULL;

    }

   

    if (initial_size == 0) {

        initial_size = PRIMES[0];

    }

   

    ht->capacity = find_nearest_prime(initial_size);

    ht->entries = calloc(ht->capacity, sizeof(hash_entry_t));

    if (ht->entries == NULL) {

        free(ht);

        return NULL;

    }

   

    init_entries(ht->entries, ht->capacity);

    ht->size = 0;

    ht->key_type = key_type;

    ht->algorithm = algo;

    ht->method = method;

    ht->max_load_factor = DEFAULT_LOAD_FACTOR;

   

    return ht;

}

/* 销毁哈希表 */

void hash_table_destroy(hash_table_t* ht) {

    if (ht == NULL) {

        return;

    }

   

    for (size_t i = 0; i < ht->capacity; i++) {

        if (ht->entries[i].status == ENTRY_OCCUPIED) {

            free_key(ht, ht->entries[i].key);

        }

       

        if (ht->method == COLLISION_CHAINING) {

            free_chaining_list(&ht->entries[i]);

        }

    }

   

    free(ht->entries);

    free(ht);

}

/* 检查是否需要扩容 */

static int needs_resize(hash_table_t* ht) {

    double load_factor = (double)ht->size / (double)ht->capacity;

    return load_factor > ht->max_load_factor;

}

/* 重新哈希 */

static int resize_table(hash_table_t* ht) {

    size_t new_capacity = find_nearest_prime(ht->capacity * 2);

    hash_entry_t* old_entries = ht->entries;

    size_t old_capacity = ht->capacity;

   

    ht->entries = calloc(new_capacity, sizeof(hash_entry_t));

    if (ht->entries == NULL) {

        ht->entries = old_entries;

        return -1;

    }

   

    init_entries(ht->entries, new_capacity);

    ht->capacity = new_capacity;

    ht->size = 0;

   

    /* 重新插入所有条目 */

    for (size_t i = 0; i < old_capacity; i++) {

        if (old_entries[i].status == ENTRY_OCCUPIED) {

            if (ht->method == COLLISION_CHAINING) {

                /* 处理链地址法 */

                hash_entry_t* entry = &old_entries[i];

                while (entry != NULL && entry->status == ENTRY_OCCUPIED) {

                    hash_table_put(ht, entry->key, entry->value);

                    hash_entry_t* next = entry->next;

                    if (entry != &old_entries[i]) {

                        free(entry);

                    }

                    entry = next;

                }

            } else {

                /* 处理开放地址法 */

                hash_table_put(ht, old_entries[i].key, old_entries[i].value);

            }

        }

    }

   

    free(old_entries);

    return 0;

}

/* 插入键值对 */

int hash_table_put(hash_table_t* ht, const void* key, void* value) {

    if (ht == NULL || key == NULL) {

        return -1;

    }

   

    /* 检查是否需要扩容 */

    if (needs_resize(ht)) {

        if (resize_table(ht) != 0) {

            return -1;

        }

    }

   

    unsigned long hash = compute_hash(ht, key);

    size_t index = compute_index(ht, hash);

    void* key_copy = copy_key(ht, key);

   

    if (key_copy == NULL) {

        return -1;

    }

   

    int result;

    if (ht->method == COLLISION_CHAINING) {

        result = chaining_insert_entry(ht, index, key_copy, value);

    } else {

        result = open_address_insert_entry(ht, index, key_copy, value);

    }

   

    if (result < 0) {

        free_key(ht, key_copy);

    }

   

    return result;

}

/* 获取值 */

void* hash_table_get(hash_table_t* ht, const void* key) {

    if (ht == NULL || key == NULL) {

        return NULL;

    }

   

    unsigned long hash = compute_hash(ht, key);

    size_t index = compute_index(ht, hash);

    hash_entry_t* entry;

   

    if (ht->method == COLLISION_CHAINING) {

        entry = chaining_find_entry(ht, index, key);

    } else {

        entry = open_address_find_entry(ht, index, key);

    }

   

    return (entry != NULL) ? entry->value : NULL;

}

/* 删除键值对 */

int hash_table_remove(hash_table_t* ht, const void* key) {

    if (ht == NULL || key == NULL) {

        return 0;

    }

   

    unsigned long hash = compute_hash(ht, key);

    size_t index = compute_index(ht, hash);

   

    if (ht->method == COLLISION_CHAINING) {

        hash_entry_t* entry = &ht->entries[index];

        hash_entry_t* prev = NULL;

       

        /* 查找要删除的条目 */

        while (entry != NULL) {

            if (entry->status == ENTRY_OCCUPIED && keys_equal(ht, entry->key, key)) {

                free_key(ht, entry->key);

               

                if (prev == NULL) {

                    /* 删除第一个条目 */

                    if (entry->next == NULL) {

                        entry->status = ENTRY_EMPTY;

                    } else {

                        /* 用下一个条目替换当前条目 */

                        hash_entry_t* next = entry->next;

                        entry->key = next->key;

                        entry->value = next->value;

                        entry->next = next->next;

                        free(next);

                    }

                } else {

                    /* 删除链表中的条目 */

                    prev->next = entry->next;

                    free(entry);

                }

               

                ht->size--;

                return 1;

            }

           

            prev = entry;

            entry = entry->next;

        }

    } else {

        /* 开放地址法:标记为删除 */

        hash_entry_t* entry = open_address_find_entry(ht, index, key);

        if (entry != NULL) {

            free_key(ht, entry->key);

            entry->status = ENTRY_DELETED;

            entry->key = NULL;

            entry->value = NULL;

            ht->size--;

            return 1;

        }

    }

   

    return 0;

}

/* 获取元素数量 */

size_t hash_table_size(hash_table_t* ht) {

    return (ht != NULL) ? ht->size : 0;

}

/* 判断是否为空 */

int hash_table_is_empty(hash_table_t* ht) {

    return (ht == NULL) || (ht->size == 0);

}

/* 清空哈希表 */

void hash_table_clear(hash_table_t* ht) {

    if (ht == NULL) {

        return;

    }

   

    for (size_t i = 0; i < ht->capacity; i++) {

        if (ht->entries[i].status == ENTRY_OCCUPIED) {

            free_key(ht, ht->entries[i].key);

            ht->entries[i].key = NULL;

            ht->entries[i].value = NULL;

            ht->entries[i].status = ENTRY_EMPTY;

        }

       

        if (ht->method == COLLISION_CHAINING) {

            free_chaining_list(&ht->entries[i]);

        }

    }

   

    ht->size = 0;

}

/* 遍历哈希表 */

int hash_table_foreach(hash_table_t* ht,

                       int (*callback)(const void* key, void* value, void* user_data),

                       void* user_data) {

    if (ht == NULL || callback == NULL) {

        return -1;

    }

   

    for (size_t i = 0; i < ht->capacity; i++) {

        if (ht->entries[i].status == ENTRY_OCCUPIED) {

            if (ht->method == COLLISION_CHAINING) {

                /* 遍历链表 */

                hash_entry_t* entry = &ht->entries[i];

                while (entry != NULL && entry->status == ENTRY_OCCUPIED) {

                    if (callback(entry->key, entry->value, user_data) != 0) {

                        return 0;

                    }

                    entry = entry->next;

                }

            } else {

                /* 开放地址法 */

                if (callback(ht->entries[i].key, ht->entries[i].value, user_data) != 0) {

                    return 0;

                }

            }

        }

    }

   

    return 0;

}

/* 获取负载因子 */

double hash_table_load_factor(hash_table_t* ht) {

    return (ht != NULL) ? (double)ht->size / (double)ht->capacity : 0.0;

}

/* 重新哈希 */

int hash_table_rehash(hash_table_t* ht, size_t new_size) {

    if (ht == NULL) {

        return -1;

    }

   

    size_t new_capacity = find_nearest_prime(new_size);

    if (new_capacity <= ht->capacity) {

        return -1; /* 新容量必须大于当前容量 */

    }

   

    return resize_table(ht);

}

main.c或test_main.c

验证哈希库功能的正确性和稳定性

特点 :

1. 全面性测试 :覆盖所有哈希算法(DJB2、BKDR、SDBM、RS)和冲突解决方法(链地址法、线性探测、二次探测)
2. 边界测试 :测试各种边界情况,如空表、满表、删除操作等
3. 性能测试 :包含大数据集测试(1000个条目)
4. 错误处理 :验证错误情况的正确处理
5. 自动化验证 :自动检查测试结果是否正确
输出特点 :详细的测试过程和验证结果,主要用于开发调试

#include "hashlib.h"

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <assert.h>

/* 测试回调函数 */

int print_callback(const void* key, void* value, void* user_data) {

    int* count = (int*)user_data;

    (*count)++;

   

    printf("  Entry %d: ", *count);

   

    /* 假设键类型为字符串进行测试 */

    printf("Key='%s', Value=%p\n", (const char*)key, value);

    return 0;

}

/* 测试整型键哈希表 */

void test_int_hash_table() {

    printf("=== Testing Integer Key Hash Table ===\n");

   

    /* 测试不同算法和冲突解决方法 */

    hash_algo_t algorithms[] = {HASH_ALGO_DJB2, HASH_ALGO_BKDR, HASH_ALGO_SDBM, HASH_ALGO_RS};

    collision_method_t methods[] = {COLLISION_CHAINING, COLLISION_LINEAR, COLLISION_QUADRATIC};

   

    for (int algo_idx = 0; algo_idx < 4; algo_idx++) {

        for (int method_idx = 0; method_idx < 3; method_idx++) {

            printf("\n--- Algorithm %d, Method %d ---\n", algo_idx, method_idx);

           

            hash_table_t* ht = hash_table_create(10, HASH_KEY_INT,

                                                algorithms[algo_idx],

                                                methods[method_idx]);

            assert(ht != NULL);

           

            /* 插入测试数据 */

            int keys[] = {1, 2, 3, 100, 200, 300, 1000, 2000, 3000};

            int values[] = {10, 20, 30, 1000, 2000, 3000, 10000, 20000, 30000};

           

            for (int i = 0; i < 9; i++) {

                int result = hash_table_put(ht, &keys[i], &values[i]);

                assert(result >= 0);

                printf("Inserted: key=%d, value=%d\n", keys[i], values[i]);

            }

           

            /* 验证插入数量 */

            assert(hash_table_size(ht) == 9);

            printf("Size: %lu\n", (unsigned long)hash_table_size(ht));

            printf("Load factor: %.3f\n", hash_table_load_factor(ht));

           

            /* 测试查找 */

            for (int i = 0; i < 9; i++) {

                int* found_value = (int*)hash_table_get(ht, &keys[i]);

                assert(found_value != NULL);

                assert(*found_value == values[i]);

                printf("Found: key=%d, value=%d\n", keys[i], *found_value);

            }

           

            /* 测试不存在的键 */

            int not_found_key = 9999;

            void* not_found = hash_table_get(ht, &not_found_key);

            assert(not_found == NULL);

            printf("Not found key %d as expected\n", not_found_key);

           

            /* 测试删除 */

            int remove_key = 100;

            int remove_result = hash_table_remove(ht, &remove_key);

            assert(remove_result == 1);

            printf("Removed key %d\n", remove_key);

           

            assert(hash_table_size(ht) == 8);

           

            /* 测试遍历 */

            printf("Hash table contents:\n");

            int count = 0;

            hash_table_foreach(ht, print_callback, &count);

           

            hash_table_destroy(ht);

            printf("Test passed for algorithm %d, method %d\n", algo_idx, method_idx);

        }

    }

   

    printf("=== Integer Key Hash Table Tests Completed ===\n\n");

}

/* 测试字符串键哈希表 */

void test_string_hash_table() {

    printf("=== Testing String Key Hash Table ===\n");

   

    /* 重点测试BKDR和DJB2算法 */

    hash_algo_t algorithms[] = {HASH_ALGO_DJB2, HASH_ALGO_BKDR};

    collision_method_t methods[] = {COLLISION_CHAINING, COLLISION_LINEAR};

   

    for (int algo_idx = 0; algo_idx < 2; algo_idx++) {

        for (int method_idx = 0; method_idx < 2; method_idx++) {

            printf("\n--- Algorithm %s, Method %d ---\n",

                   (algorithms[algo_idx] == HASH_ALGO_DJB2) ? "DJB2" : "BKDR",

                   method_idx);

           

            hash_table_t* ht = hash_table_create(10, HASH_KEY_STR,

                                                algorithms[algo_idx],

                                                methods[method_idx]);

            assert(ht != NULL);

           

            /* 插入测试数据 */

            const char* keys[] = {

                "apple", "banana", "cherry", "date", "elderberry",

                "fig", "grape", "honeydew", "kiwi", "lemon"

            };

           

            int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

           

            for (int i = 0; i < 10; i++) {

                int result = hash_table_put(ht, keys[i], &values[i]);

                assert(result >= 0);

                printf("Inserted: key='%s', value=%d\n", keys[i], values[i]);

            }

           

            /* 验证插入数量 */

            assert(hash_table_size(ht) == 10);

            printf("Size: %lu\n", (unsigned long)hash_table_size(ht));

            printf("Load factor: %.3f\n", hash_table_load_factor(ht));

           

            /* 测试哈希算法函数 */

            if (algorithms[algo_idx] == HASH_ALGO_DJB2) {

                unsigned long hash1 = hash_djb2("test");

                unsigned long hash2 = hash_djb2("test");

                assert(hash1 == hash2);

                printf("DJB2 hash consistency test passed\n");

            } else {

                unsigned long hash1 = hash_bkdr("test");

                unsigned long hash2 = hash_bkdr("test");

                assert(hash1 == hash2);

                printf("BKDR hash consistency test passed\n");

            }

           

            /* 测试查找 */

            for (int i = 0; i < 10; i++) {

                int* found_value = (int*)hash_table_get(ht, keys[i]);

                assert(found_value != NULL);

                assert(*found_value == values[i]);

                printf("Found: key='%s', value=%d\n", keys[i], *found_value);

            }

           

            /* 测试不存在的键 */

            const char* not_found_key = "mango";

            void* not_found = hash_table_get(ht, not_found_key);

            assert(not_found == NULL);

            printf("Not found key '%s' as expected\n", not_found_key);

           

            /* 测试删除 */

            const char* remove_key = "banana";

            int remove_result = hash_table_remove(ht, remove_key);

            assert(remove_result == 1);

            printf("Removed key '%s'\n", remove_key);

           

            assert(hash_table_size(ht) == 9);

           

            /* 测试更新 */

            int new_value = 99;

            int update_result = hash_table_put(ht, "apple", &new_value);

            assert(update_result == 0); /* 0表示更新,1表示插入 */

           

            int* updated_value = (int*)hash_table_get(ht, "apple");

            assert(updated_value != NULL);

            assert(*updated_value == 99);

            printf("Updated 'apple' to value %d\n", *updated_value);

           

            hash_table_destroy(ht);

            printf("Test passed for algorithm %s, method %d\n",

                   (algorithms[algo_idx] == HASH_ALGO_DJB2) ? "DJB2" : "BKDR",

                   method_idx);

        }

    }

   

    printf("=== String Key Hash Table Tests Completed ===\n\n");

}

/* 测试结构体键哈希表 */

void test_struct_hash_table() {

    printf("=== Testing Struct Key Hash Table ===\n");

   

    /* 定义测试结构体 */

    typedef struct {

        int id;

        char name[20];

    } test_struct_t;

   

    /* 使用指针作为键 */

    hash_table_t* ht = hash_table_create(10, HASH_KEY_PTR,

                                        HASH_ALGO_DJB2,

                                        COLLISION_CHAINING);

    assert(ht != NULL);

   

    /* 创建测试结构体 */

    test_struct_t structs[5];

    int values[] = {100, 200, 300, 400, 500};

   

    for (int i = 0; i < 5; i++) {

        structs[i].id = i + 1;

        snprintf(structs[i].name, sizeof(structs[i].name), "struct_%d", i + 1);

       

        int result = hash_table_put(ht, &structs[i], &values[i]);

        assert(result >= 0);

        printf("Inserted: struct{id=%d, name='%s'}, value=%d\n",

               structs[i].id, structs[i].name, values[i]);

    }

   

    /* 验证插入数量 */

    assert(hash_table_size(ht) == 5);

    printf("Size: %lu\n", (unsigned long)hash_table_size(ht));

   

    /* 测试查找 */

    for (int i = 0; i < 5; i++) {

        int* found_value = (int*)hash_table_get(ht, &structs[i]);

        assert(found_value != NULL);

        assert(*found_value == values[i]);

        printf("Found: struct{id=%d, name='%s'}, value=%d\n",

               structs[i].id, structs[i].name, *found_value);

    }

   

    /* 测试清空 */

    hash_table_clear(ht);

    assert(hash_table_size(ht) == 0);

    assert(hash_table_is_empty(ht));

    printf("Hash table cleared successfully\n");

   

    hash_table_destroy(ht);

    printf("=== Struct Key Hash Table Tests Completed ===\n\n");

}

/* 测试性能和大数据量 */

void test_performance() {

    printf("=== Testing Performance with Large Dataset ===\n");

   

    /* 测试链地址法 */

    hash_table_t* ht_chaining = hash_table_create(100, HASH_KEY_STR,

                                                 HASH_ALGO_BKDR,

                                                 COLLISION_CHAINING);

    assert(ht_chaining != NULL);

   

    /* 插入1000个字符串键值对 */

    int inserted_count = 0;

    for (int i = 0; i < 1000; i++) {

        char key[20];

        snprintf(key, sizeof(key), "key_%d", i);

       

        int* value = malloc(sizeof(int));

        *value = i * 10;

       

        if (hash_table_put(ht_chaining, key, value) >= 0) {

            inserted_count++;

        }

    }

   

    printf("Inserted %d entries with chaining method\n", inserted_count);

    printf("Final size: %lu\n", (unsigned long)hash_table_size(ht_chaining));

    printf("Final load factor: %.3f\n", hash_table_load_factor(ht_chaining));

   

    /* 验证所有插入的数据 */

    int verified_count = 0;

    for (int i = 0; i < 1000; i++) {

        char key[20];

        snprintf(key, sizeof(key), "key_%d", i);

       

        int* found_value = (int*)hash_table_get(ht_chaining, key);

        if (found_value != NULL && *found_value == i * 10) {

            verified_count++;

        }

    }

   

    printf("Verified %d entries\n", verified_count);

   

    /* 清理内存 */

    for (int i = 0; i < 1000; i++) {

        char key[20];

        snprintf(key, sizeof(key), "key_%d", i);

       

        int* value = (int*)hash_table_get(ht_chaining, key);

        if (value != NULL) {

            free(value);

        }

    }

   

    hash_table_destroy(ht_chaining);

   

    /* 测试开放地址法 */

    hash_table_t* ht_open = hash_table_create(100, HASH_KEY_INT,

                                             HASH_ALGO_DJB2,

                                             COLLISION_LINEAR);

    assert(ht_open != NULL);

   

    /* 插入500个整型键值对 */

    inserted_count = 0;

    for (int i = 0; i < 500; i++) {

        int key = i * 2; /* 使用偶数键 */

        int* value = malloc(sizeof(int));

        *value = i * 5;

       

        if (hash_table_put(ht_open, &key, value) >= 0) {

            inserted_count++;

        }

    }

   

    printf("Inserted %d entries with open addressing method\n", inserted_count);

    printf("Final size: %lu\n", (unsigned long)hash_table_size(ht_open));

    printf("Final load factor: %.3f\n", hash_table_load_factor(ht_open));

   

    /* 清理内存 */

    for (int i = 0; i < 500; i++) {

        int key = i * 2;

        int* value = (int*)hash_table_get(ht_open, &key);

        if (value != NULL) {

            free(value);

        }

    }

   

    hash_table_destroy(ht_open);

   

    printf("=== Performance Tests Completed ===\n\n");

}

/* 测试哈希算法函数 */

void test_hash_functions() {

    printf("=== Testing Hash Functions ===\n");

   

    const char* test_strings[] = {

        "hello", "world", "hash", "table", "test",

        "algorithm", "djb2", "bkdr", "sdbm", "rs"

    };

   

    printf("Testing hash functions consistency:\n");

   

    for (int i = 0; i < 10; i++) {

        unsigned long djb2_1 = hash_djb2(test_strings[i]);

        unsigned long djb2_2 = hash_djb2(test_strings[i]);

        unsigned long bkdr_1 = hash_bkdr(test_strings[i]);

        unsigned long bkdr_2 = hash_bkdr(test_strings[i]);

        unsigned long sdbm_1 = hash_sdbm(test_strings[i]);

        unsigned long sdbm_2 = hash_sdbm(test_strings[i]);

        unsigned long rs_1 = hash_rs(test_strings[i]);

        unsigned long rs_2 = hash_rs(test_strings[i]);

       

        assert(djb2_1 == djb2_2);

        assert(bkdr_1 == bkdr_2);

        assert(sdbm_1 == sdbm_2);

        assert(rs_1 == rs_2);

       

        printf("  '%s': DJB2=%lu, BKDR=%lu, SDBM=%lu, RS=%lu\n",

               test_strings[i], djb2_1, bkdr_1, sdbm_1, rs_1);

    }

   

    printf("All hash functions show consistent results\n");

    printf("=== Hash Functions Tests Completed ===\n\n");

}

int main() {

    printf("Starting Hash Library Tests...\n\n");

   

    /* 运行所有测试 */

    test_hash_functions();

    test_int_hash_table();

    test_string_hash_table();

    test_struct_hash_table();

    test_performance();

   

    printf("All tests completed successfully!\n");

    printf("Hash library is working correctly.\n");

   

    return 0;

}

example.c

主要目的 :展示哈希库的实际使用方法和应用场景

特点 :

1.  实用示例 :提供6个实际应用场景
2.  展示如何正确使用API函数
3.  场景化 :每个示例针对特定使用场景
   - 字符串键与价格管理
   - 整型键与学生成绩管理
   - 结构体键与几何点管理
   - 遍历功能与人口统计
   - 重新哈希与性能优化
   - 哈希算法比较
4.  用户友好 :输出结果易于理解,展示实际应用价值

#include "hashlib.h"

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

/* 示例1:使用字符串键和BKDR算法 */

void example_string_keys() {

    printf("=== Example 1: String Keys with BKDR Algorithm ===\n");

   

    /* 创建哈希表 */

    hash_table_t* ht = hash_table_create(0, HASH_KEY_STR, HASH_ALGO_BKDR, COLLISION_CHAINING);

   

    /* 插入一些水果和它们的价格 */

    int apple_price = 5;

    int banana_price = 3;

    int cherry_price = 8;

   

    hash_table_put(ht, "apple", &apple_price);

    hash_table_put(ht, "banana", &banana_price);

    hash_table_put(ht, "cherry", &cherry_price);

   

    printf("Inserted 3 fruits with prices\n");

    printf("Current size: %lu\n", (unsigned long)hash_table_size(ht));

    printf("Load factor: %.3f\n", hash_table_load_factor(ht));

   

    /* 查找价格 */

    int* price = (int*)hash_table_get(ht, "apple");

    if (price) {

        printf("Price of apple: %d\n", *price);

    }

   

    price = (int*)hash_table_get(ht, "banana");

    if (price) {

        printf("Price of banana: %d\n", *price);

    }

   

    /* 尝试查找不存在的键 */

    price = (int*)hash_table_get(ht, "mango");

    if (!price) {

        printf("Mango not found (as expected)\n");

    }

   

    hash_table_destroy(ht);

    printf("\n");

}

/* 示例2:使用整型键和线性探测 */

void example_int_keys() {

    printf("=== Example 2: Integer Keys with Linear Probing ===\n");

   

    /* 创建哈希表 */

    hash_table_t* ht = hash_table_create(10, HASH_KEY_INT, HASH_ALGO_DJB2, COLLISION_LINEAR);

   

    /* 插入学生ID和成绩 */

    int student_grades[] = {85, 92, 78, 96, 88};

    int student_ids[] = {1001, 1002, 1003, 1004, 1005};

   

    for (int i = 0; i < 5; i++) {

        hash_table_put(ht, &student_ids[i], &student_grades[i]);

    }

   

    printf("Inserted 5 student records\n");

   

    /* 查找特定学生的成绩 */

    int search_id = 1003;

    int* grade = (int*)hash_table_get(ht, &search_id);

    if (grade) {

        printf("Student %d grade: %d\n", search_id, *grade);

    }

   

    /* 更新成绩 */

    int new_grade = 82;

    hash_table_put(ht, &search_id, &new_grade);

   

    grade = (int*)hash_table_get(ht, &search_id);

    if (grade) {

        printf("Updated student %d grade: %d\n", search_id, *grade);

    }

   

    hash_table_destroy(ht);

    printf("\n");

}

/* 示例3:使用结构体作为键 */

void example_struct_keys() {

    printf("=== Example 3: Struct Keys ===\n");

   

    /* 定义坐标结构体 */

    typedef struct {

        int x;

        int y;

    } Point;

   

    /* 创建哈希表 */

    hash_table_t* ht = hash_table_create(0, HASH_KEY_PTR, HASH_ALGO_SDBM, COLLISION_CHAINING);

   

    /* 创建一些点 */

    Point p1 = {1, 2};

    Point p2 = {3, 4};

    Point p3 = {5, 6};

   

    char* descriptions[] = {

        "First point", "Second point", "Third point"

    };

   

    hash_table_put(ht, &p1, descriptions[0]);

    hash_table_put(ht, &p2, descriptions[1]);

    hash_table_put(ht, &p3, descriptions[2]);

   

    printf("Inserted 3 points with descriptions\n");

   

    /* 查找点的描述 */

    char* desc = (char*)hash_table_get(ht, &p2);

    if (desc) {

        printf("Point (%d, %d): %s\n", p2.x, p2.y, desc);

    }

   

    hash_table_destroy(ht);

    printf("\n");

}

/* 示例4:遍历哈希表 */

void example_traversal() {

    printf("=== Example 4: Hash Table Traversal ===\n");

   

    /* 创建哈希表 */

    hash_table_t* ht = hash_table_create(0, HASH_KEY_STR, HASH_ALGO_BKDR, COLLISION_CHAINING);

   

    /* 插入一些数据 */

    char* countries[] = {"China", "USA", "Japan", "Germany", "France"};

    int populations[] = {1441, 331, 126, 83, 68}; // 百万

   

    for (int i = 0; i < 5; i++) {

        hash_table_put(ht, countries[i], &populations[i]);

    }

   

    printf("Country populations (in millions):\n");

   

    /* 定义遍历回调函数 */

    int print_population(const void* key, void* value, void* user_data) {

        int* total = (int*)user_data;

        (*total) += *(int*)value;

        printf("  %s: %d million\n", (const char*)key, *(int*)value);

        return 0; // 继续遍历

    }

   

    int total_population = 0;

    hash_table_foreach(ht, print_population, &total_population);

   

    printf("Total population: %d million\n", total_population);

   

    hash_table_destroy(ht);

    printf("\n");

}

/* 示例5:性能测试和重新哈希 */

void example_rehashing() {

    printf("=== Example 5: Rehashing and Performance ===\n");

   

    /* 创建小容量的哈希表 */

    hash_table_t* ht = hash_table_create(5, HASH_KEY_STR, HASH_ALGO_DJB2, COLLISION_CHAINING);

   

    printf("Initial load factor: %.3f\n", hash_table_load_factor(ht));

   

    /* 插入多个条目触发重新哈希 */

    for (int i = 0; i < 20; i++) {

        char key[20];

        snprintf(key, sizeof(key), "item_%d", i);

       

        int* value = malloc(sizeof(int));

        *value = i * 10;

       

        hash_table_put(ht, key, value);

    }

   

    printf("After inserting 20 items:\n");

    printf("Size: %lu\n", (unsigned long)hash_table_size(ht));

    printf("Load factor: %.3f\n", hash_table_load_factor(ht));

   

    /* 手动重新哈希到更大容量 */

    printf("\nManual rehashing to capacity 50...\n");

    hash_table_rehash(ht, 50);

   

    printf("After rehashing:\n");

    printf("Load factor: %.3f\n", hash_table_load_factor(ht));

   

    /* 清理内存 */

    for (int i = 0; i < 20; i++) {

        char key[20];

        snprintf(key, sizeof(key), "item_%d", i);

       

        int* value = (int*)hash_table_get(ht, key);

        if (value) {

            free(value);

        }

    }

   

    hash_table_destroy(ht);

    printf("\n");

}

/* 示例6:比较不同哈希算法 */

void example_hash_comparison() {

    printf("=== Example 6: Hash Algorithm Comparison ===\n");

   

    const char* test_strings[] = {

        "hello", "world", "programming", "algorithm", "data"

    };

   

    printf("Hash values for test strings:\n");

    printf("String\t\tDJB2\t\tBKDR\t\tSDBM\t\tRS\n");

    printf("------\t\t----\t\t----\t\t----\t\t--\n");

   

    for (int i = 0; i < 5; i++) {

        unsigned long djb2 = hash_djb2(test_strings[i]);

        unsigned long bkdr = hash_bkdr(test_strings[i]);

        unsigned long sdbm = hash_sdbm(test_strings[i]);

        unsigned long rs = hash_rs(test_strings[i]);

       

        printf("%s\t%lu\t%lu\t%lu\t%lu\n",

               test_strings[i], djb2, bkdr, sdbm, rs);

    }

    printf("\n");

}

int main() {

    printf("Hash Library Examples\n");

    printf("====================\n\n");

   

    /* 运行所有示例 */

    example_string_keys();

    example_int_keys();

    example_struct_keys();

    example_traversal();

    example_rehashing();

    example_hash_comparison();

   

    printf("All examples completed successfully!\n");

   

    return 0;

}

七、面试常见问题

1. 哈希表的基本原理是什么?

  • 回答要点

    • 哈希表是一个用于快速查找、插入和删除数据的容器结构。

    • 核心是通过哈希函数将键值映射到数组的索引位置。处理冲突有两种常见方法:链地址法(Separate Chaining)和开放寻址法(Open Addressing)。

2. 哈希表如何处理冲突?

  • 回答要点

    • 链地址法(拉链法):每个桶维护一个链表(或其他数据结构)来存储冲突的元素。

    • 开放寻址法:当冲突发生时,探查下一个空位置来插入元素,常见策略有线性探查、二次探查、双重哈希等。

3. 哈希函数的作用及常见的哈希算法有哪些?

  • 回答要点

    • 哈希函数将数据的键映射到哈希表的索引位置。

    • 常见哈希算法:DJB2(针对字符串),MurmurHashSHA系列(适用于更复杂的数据集),以及整型数据常用的Knuth乘法哈希

    • 哈希函数需要满足:快速计算、均匀分布、避免碰撞。

4. 哈希表的负载因子是什么?如何影响哈希表性能?

  • 回答要点

    • 元素个数指的是哈希表中实际存储的键值对的数量,而不是哈希表的桶的数量。

      具体来说:元素个数(即 nentries)是哈希表中存储的有效数据项的数量。每个键值对都会占用一个桶中的位置,可能会有冲突,但每个有效的键值对都算作一个元素。在哈希表的扩容或缩容过程中,通常会根据当前的元素个数来决定是否需要进行重新哈希(rehash),即扩展哈希表的大小。举个例子:假设你有一个哈希表大小为 10 的桶数组(nbuckets = 10),当你插入了 7 个元素时,哈希表的元素个数就是 7,即:哈希表中的 元素个数7   哈希表的 桶数组大小10

    • 负载因子 = 元素个数 / 哈希表(桶数组)大小。较高的负载因子意味着哈希表中的元素较多,可能会导致性能下降。

    • 当负载因子达到一定阈值时,通常会进行扩容(rehash),以保持性能。

5. 哈希表的扩容是如何实现的?

  • 回答要点

    • 扩容时,哈希表大小通常会翻倍(或选择下一个质数)。扩容后,需要重新计算每个元素的哈希值并重新分配到新表中。

6. 哈希表在嵌入式开发中如何优化内存和性能?

  • 回答要点

    • 内存优化:在嵌入式开发中,通常需要限制内存使用。可以选择较小的哈希表大小和合适的负载因子(通常在0.6到0.75之间),并使用固定大小的桶数组来避免内存碎片。

    • 目标(在嵌入式中的优先级)

    • 最小化运行时堆/碎片,尽量使用静态/一次性分配。

    • 保证查找性能可预测(避免最坏情况长时间阻塞)。

    • 核心策略概览(优先级从高到低)

    • 优先静态分配(固定大小):bucket 数组 + entry 池,避免运行时 malloc/free

    • 选择合适的冲突解决方式:嵌入式常用 开放寻址法(线性/二次/双哈希)链式 + 预分配节点池(intrusive 链表)。开放寻址通常内存更紧凑、cache 友好;链式对删除更友好、但需指针开销。

    • 降低每条记录的内存开销:使用紧凑结构(按需字段、用索引替代指针、短整型等)。

    • 避免频繁扩容 / 禁用自动 rehash:在嵌入式常设定固定容量或只允许手动 rehash。

    • 合理选择负载因子:开放寻址建议 ≤ 0.6;链式可稍高(0.75~1.0)。

    • 使用字符串驻留或索引池:若键为字符串,避免 strdup 每次分配,使用字符串池或只存索引/指针到常量区。

    • 使用内存池 / slab 分配器:如果必须动态分配,使用固定大小对象池避免碎片。

    • 对齐与 packing:用 packed 或显式字段顺序减少 padding;但留意性能影响。

    • 避免递归/复杂对象在哈希表内:存储简单指针或数值,复杂对象放外部管理。

    • 尽量计算内存占用公式并在编译期/启动时验证

    • 内存使用可估算、可控(便于通过linker map 评估)。

    • 允许必要时牺牲一点灵活性(例如不自动扩容)换取内存确定性。

    • 性能优化:使用高效的哈希算法(如DJB2),并考虑冲突处理方法。在某些情况下,使用开放寻址法可能更节省内存。

    • 避免过度扩容:使用合适的扩容策略,避免频繁的re-hash操作。

7. 哈希表的空间复杂度和时间复杂度是多少?

  • 回答要点

    • 空间复杂度:O(n),n是元素个数。每个元素占用一定的内存空间,哈希表的桶数组也占用内存。

    • 时间复杂度

      • 查找、插入、删除:O(1)(平均情况),最坏情况下可能达到O(n)(当所有元素都映射到同一个桶时)。

      • 扩容操作:O(n)(重哈希时需要重新计算所有元素的位置)。

8. 在哈希表中,如何判断一个键是否存在?

  • 回答要点

    • 查找时,首先计算该键的哈希值,然后根据哈希值定位到相应的桶。如果桶中存在该键,则返回对应的值,否则返回“未找到”。

9. 为什么哈希表的最坏时间复杂度是O(n)?

  • 回答要点

    • 哈希表的最坏情况发生在哈希函数的碰撞非常严重时,所有元素都被映射到同一个桶。此时,查找、插入和删除操作都变成了遍历桶内链表的操作,时间复杂度退化为O(n)。

10. 哈希表在多线程环境中的问题是什么?

  • 回答要点

    • 哈希表在多线程环境下的主要问题是数据竞争。为确保线程安全,需要加锁或使用其他同步机制。

    • 如果不使用线程安全的哈希表,可以选择对每个桶进行加锁,或者使用其他并发哈希表实现。

11. 如何选择哈希表的大小和负载因子?

  • 回答要点

    • 哈希表大小:通常选择一个质数作为哈希表的大小,避免哈希冲突。

    • 负载因子:负载因子控制了哈希表的扩容时机。一个常见的策略是将负载因子设置为0.75,表示哈希表元素达到75%时进行扩容。

12. 哈希表与字典(map)或集合(set)的区别?

  • 回答要点

    • 哈希表通常是一个键值对存储的容器,用于存储和快速查找数据。

    • 字典(或map)是哈希表的一个应用,它实现了基于哈希表的键值对映射。

    • 集合(set)通常只存储,没有值,但本质上也是基于哈希表实现的。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐