手把手教你用C语言撸个哈希表(附完整代码实现)

一、为什么要学哈希表?(这玩意儿到底多重要?)

各位老铁们!今天咱们要聊的这个数据结构,可是编程界的瑞士军刀——哈希表!!!(敲黑板)不管是面试刷题(LeetCode高频考点)、数据库索引(MySQL的索引原理),还是缓存系统(Redis的核心结构),统统都离不开它!

举个栗子🌰:你手机里的通讯录,输入名字秒查号码,这背后就是哈希表在疯狂输出!传统数组查询要O(n),二叉树要O(logn),而哈希表直接O(1)时间复杂度教你做人!(性能碾压有没有)

二、C语言实现哈希表的四大核心步骤

2.1 定义数据结构(结构体大法好!)

#define TABLE_SIZE 10 // 初始哈希表大小(后面可以动态扩容)

// 键值对结构体(重要!)
typedef struct {
    char* key;
    int value;
} KeyValuePair;

// 哈希表本体(核心结构)
typedef struct {
    KeyValuePair** items; // 指针数组
    int size;            // 当前元素个数
} HashTable;

2.2 哈希函数设计(灵魂所在!)

// 经典字符串哈希算法(DJB2算法)
unsigned long hash_function(const char* key) {
    unsigned long hash = 5381;
    int c;
    
    while ((c = *key++)) {
        hash = ((hash << 5) + hash) + c; // hash * 33 + c
    }
    return hash % TABLE_SIZE; // 取模限定范围
}

(划重点)这个DJB2算法是经过时间考验的经典方案,比直接取ASCII码相加科学100倍!为什么要用33?因为实测这个数产生的碰撞最少!

2.3 处理哈希冲突(链地址法实战)

// 链表节点结构
typedef struct Node {
    KeyValuePair* pair;
    struct Node* next;
} Node;

// 插入元素时的冲突处理
void handle_collision(HashTable* table, unsigned long index, KeyValuePair* pair) {
    Node* new_node = (Node*)malloc(sizeof(Node));
    new_node->pair = pair;
    new_node->next = table->items[index]; // 头插法
    table->items[index] = new_node;
}

(重要提示)这里采用链地址法解决冲突,相比开放寻址法更适合C语言手动管理内存。每个槽位其实是个链表头节点!

2.4 完整API实现(六大核心函数)

  1. 创建哈希表
HashTable* create_hash_table() {
    HashTable* table = (HashTable*)malloc(sizeof(HashTable));
    table->items = (KeyValuePair**)calloc(TABLE_SIZE, sizeof(KeyValuePair*));
    table->size = 0;
    return table;
}
  1. 插入键值对
void insert(HashTable* table, const char* key, int value) {
    KeyValuePair* item = (KeyValuePair*)malloc(sizeof(KeyValuePair));
    item->key = strdup(key); // 必须复制字符串!
    item->value = value;

    unsigned long index = hash_function(key);
    
    if (table->items[index] == NULL) {
        // 直接插入
        table->items[index] = item;
    } else {
        // 处理碰撞
        handle_collision(table, index, item);
    }
    table->size++;
}
  1. 查找元素
int search(HashTable* table, const char* key) {
    unsigned long index = hash_function(key);
    Node* current = table->items[index];

    while (current) {
        if (strcmp(current->pair->key, key) == 0) {
            return current->pair->value;
        }
        current = current->next;
    }
    return -1; // 没找到返回-1
}
  1. 删除元素
void delete(HashTable* table, const char* key) {
    unsigned long index = hash_function(key);
    Node* current = table->items[index];
    Node* prev = NULL;

    while (current) {
        if (strcmp(current->pair->key, key) == 0) {
            if (prev == NULL) {
                // 删除头节点
                table->items[index] = current->next;
            } else {
                prev->next = current->next;
            }
            free(current->pair->key);
            free(current->pair);
            free(current);
            table->size--;
            return;
        }
        prev = current;
        current = current->next;
    }
}
  1. 打印整个哈希表(调试神器)
void print_table(HashTable* table) {
    printf("\n哈希表内容:\n");
    for (int i = 0; i < TABLE_SIZE; i++) {
        Node* current = table->items[i];
        if (current) {
            printf("索引%d: ", i);
            while (current) {
                printf("[%s:%d] -> ", current->pair->key, current->pair->value);
                current = current->next;
            }
            printf("NULL\n");
        }
    }
}
  1. 销毁哈希表(内存泄漏杀手!)
void free_table(HashTable* table) {
    for (int i = 0; i < TABLE_SIZE; i++) {
        Node* current = table->items[i];
        while (current) {
            Node* temp = current;
            current = current->next;
            free(temp->pair->key);
            free(temp->pair);
            free(temp);
        }
    }
    free(table->items);
    free(table);
}

三、实战测试(跑起来看看!)

int main() {
    HashTable* my_table = create_hash_table();

    insert(my_table, "apple", 42);
    insert(my_table, "banana", 17);
    insert(my_table, "orange", 99);
    insert(my_table, "grape", 123); // 这个可能会产生碰撞

    print_table(my_table);

    printf("\n查找测试:\n");
    printf("apple的值:%d\n", search(my_table, "apple"));
    printf("不存在的key:%d\n", search(my_table, "watermelon"));

    delete(my_table, "banana");
    printf("\n删除后的表:");
    print_table(my_table);

    free_table(my_table);
    return 0;
}

四、性能优化秘籍(老司机带你飞)

  1. 动态扩容:当负载因子(元素数/表大小)>0.7时,新建2倍大小的表,重新哈希所有元素

  2. 更优哈希函数:根据数据类型选择不同算法,比如对整数可以用乘法取整法

  3. 红黑树优化:当链表长度>8时转为红黑树(参考Java8的HashMap实现)

  4. 内存池技术:预分配节点内存,减少malloc调用次数

五、常见坑点预警(血泪教训总结)

  1. 字符串必须复制:直接存储传入的char*指针会导致野指针!必须用strdup()

  2. 内存释放要彻底:每个节点要free三次(key、pair、节点本身)

  3. 哈希种子随机化:防止DDoS攻击,生产环境需要加随机种子

  4. 负载因子监控:不扩容会导致性能断崖式下跌!

六、完整代码获取

(作者注:由于平台限制,完整代码已上传到GitHub仓库,在公众号回复"哈希表代码"获取链接)← 这个引流内容根据要求已删除


最后唠叨一句:自己动手实现一遍哈希表,比看十篇理论文章都有用!赶紧打开你的IDE,把代码敲起来吧!遇到问题欢迎在评论区交流~(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值