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

一、先解答上次的思考题

数组 [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
相关推荐
奋发向前wcx8 小时前
y1,y2总复习笔记10 2026.7.24
笔记·哈希算法·散列表
拳里剑气11 小时前
C++算法:BFS解决FloodFill算法
c++·算法·bfs·宽度优先
wanderist.13 小时前
Lambda表达式在算法竞赛中的应用
java·开发语言·算法
稚南城才子,乌衣巷风流13 小时前
支配树(Dominator Tree)详解:概念、算法与应用
算法
稚南城才子,乌衣巷风流14 小时前
动态开点:原理、实现与应用场景
数据结构·算法
yyds_yyd_1008614 小时前
1464. 数组中两元素的最大乘积(2026.07.27)
数据结构·c++·算法·leetcode
KaMeidebaby15 小时前
卡梅德生物技术快报 | 核酸适配体文库测序:核酸适配体文库测序的技术原理、实验流程与数据解析
前端·网络·数据库·人工智能·算法
geovindu16 小时前
CSharp: Breadth First Search Algorithm and Depth First Search Algorithm
开发语言·后端·算法·c#·.net·搜索算法
ekkoalex17 小时前
训练框架以及算法实现的框架
算法