哈希表实战:从原理到手写实现

一、先解答上次的思考题

数组 [5,2,8,1,3] 建成大顶堆后,依次取出堆顶结果:8 5 3 2 1


二、今天学习目标

  1. 什么是哈希表、哈希函数、哈希冲突
  2. 解决冲突:链地址法(拉链法)
  3. 手写实现一个简易哈希表
  4. 支持:插入、查找、删除操作

三、什么是哈希表?

哈希表(Hash Table)也叫散列表 ,核心思想:用 key 直接计算出存储位置,实现近乎 O (1) 的查找。

  • 数组:下标是数字,查找 O (1)
  • 哈希表:key 可以是数字 / 字符串,通过哈希函数转成数组下标

关键概念

  1. 哈希函数 :把 key 转成数组下标最简单:index = key % 表长度
  2. 哈希冲突:两个不同 key 算出同一个 index
  3. 冲突解决 :最常用 链地址法(同一位置挂一条链表)

四、结构设计(链地址法)

  • 数组作为哈希桶
  • 每个桶是一个链表,存放冲突的元素
cpp 复制代码
哈希表结构示意:
[0] → (key,val) → NULL
[1] → NULL
[2] → (key,val) → (key,val) → NULL
...

五、完整可运行代码(简易哈希表)

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

// 哈希表大小
#define SIZE 10

// 链表节点
struct Node {
    int key;
    int val;
    struct Node* next;
};

// 哈希表:数组存链表头
struct Node* hashTable[SIZE];

// 初始化哈希表
void initHashTable() {
    for (int i = 0; i < SIZE; i++)
        hashTable[i] = NULL;
}

// 哈希函数
int hashFunc(int key) {
    return key % SIZE;
}

// 插入 key-value
void insert(int key, int val) {
    int index = hashFunc(key);
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->key = key;
    newNode->val = val;
    newNode->next = NULL;

    // 头插法
    newNode->next = hashTable[index];
    hashTable[index] = newNode;
}

// 查找:根据 key 找 val,找到返回1,否则0
int find(int key, int* resVal) {
    int index = hashFunc(key);
    struct Node* cur = hashTable[index];
    while (cur != NULL) {
        if (cur->key == key) {
            *resVal = cur->val;
            return 1;
        }
        cur = cur->next;
    }
    return 0;
}

// 删除 key
void delete(int key) {
    int index = hashFunc(key);
    struct Node* cur = hashTable[index];
    struct Node* pre = NULL;

    while (cur != NULL && cur->key != key) {
        pre = cur;
        cur = cur->next;
    }
    if (cur == NULL) return;

    if (pre == NULL)
        hashTable[index] = cur->next;
    else
        pre->next = cur->next;

    free(cur);
}

// 打印哈希表
void printHashTable() {
    for (int i = 0; i < SIZE; i++) {
        printf("[%d] ", i);
        struct Node* cur = hashTable[i];
        while (cur != NULL) {
            printf("→ (%d:%d) ", cur->key, cur->val);
            cur = cur->next;
        }
        printf("\n");
    }
}

// ==================== 主函数测试 ====================
int main() {
    initHashTable();

    insert(1, 100);
    insert(2, 200);
    insert(11, 1100);  // 11%10=1,与1冲突
    insert(21, 2100);  // 21%10=1,继续冲突

    printf("哈希表内容:\n");
    printHashTable();

    int val;
    if (find(11, &val))
        printf("\n找到 key=11,val=%d\n", val);

    delete(11);
    printf("\n删除 key=11 后:\n");
    printHashTable();

    return 0;
}

六、运行结果

cpp 复制代码
哈希表内容:
[0]
[1] → (21:2100) → (11:1100) → (1:100)
[2] → (2:200)
[3]
[4]
[5]
[6]
[7]
[8]
[9]

找到 key=11,val=1100

删除 key=11 后:
[0]
[1] → (21:2100) → (1:100)
[2] → (2:200)
[3]
[4]
[5]
[6]
[7]
[8]
[9]

七、哈希表总结

  • 查找 / 插入 / 删除平均复杂度:O(1)
  • 冲突最坏情况退化为链表:O(n)
  • 工程常用:unordered_map、Redis 哈希、数据库索引

八、今日小练习

  1. 插入 key:5,15,25,7
  2. 打印哈希表,观察冲突位置
  3. 查找 key=15,删除 key=5
相关推荐
通信仿真爱好者14 小时前
第【60期】--大规模MIMO系统信号检测算法误码率比较 --matlab完整代码+参考文章
算法·matlab·mimo·信号检测
AI科技星14 小时前
全域谱分析:无穷维超复数信息场分形统一场论——自然、量子、金融多重分形第一性原理完整体系(中英双语终稿)
人工智能·python·算法·金融·乖乖数学·全域数学
先吃饱再说15 小时前
为什么堆能 O(log n) 插入?拆解完全二叉树的数组魔法
算法·排序算法
留白_16 小时前
【决策树】泰坦尼克号生存预测
算法·决策树·机器学习
hqzing16 小时前
鸿蒙 PC 底层开发技术详解(七):二进制自签名算法的实现
算法·华为·harmonyos
AI科技星16 小时前
超复数全域经济周期场与信息谱场——金融与密码学底层理论重构《0·1·∞三元一体全域超复数统一场论》系列全集(六一字不漏完整合订终版)
人工智能·算法·金融·密码学·拓扑学·乖乖数学·全域数学
AI科技星16 小时前
《01无穷全域信息场论:算子G与宇宙本体高维完备公理大典》
人工智能·python·算法·金融·乖乖数学·全域数学
Tim_1016 小时前
【C++】013、空类占多少字节?
算法
Ricky_Theseus17 小时前
并查集:连通性问题
算法
Tairitsu_H17 小时前
[LC优选算法#18] 前缀和 | 除⾃⾝以外数组的乘积 | 和为K的⼦数组 | 和可被K整除的⼦数组
算法·前缀和·哈希算法