数据结构 哈希表(链地址法)

头文件

cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define INITIAL_CAPACITY 16
#define LOAD_FACTOR_THRESHOLD 0.75

// 哈希表节点(链表节点)
typedef struct HashNode {
    char* key;            // 键(字符串)
    int value;            // 值(整数)
    struct HashNode* next;// 下一个节点指针
} HashNode;
// 哈希表结构
typedef struct HashTable {
    HashNode** buckets;   // 桶数组(每个桶是一个链表头)
    int capacity;         // 桶的数量(哈希表容量)
    int size;             // 当前元素数量
    double load_factor;   // 负载因子阈值
} HashTable;

// 1. 创建哈希表
HashTable* createHashTable();

// 2. 销毁哈希表
void destroyHashTable(HashTable* table);

// 3. 插入键值对
int hashInsert(HashTable* table, const char* key, int value);

// 4. 查找键对应的值
int* hashSearch(HashTable* table, const char* key);

// 5. 删除键值对
int hashDelete(HashTable* table, const char* key);

// 6. 获取哈希表大小
int getSize(HashTable* table);

// 7. 判断哈希表是否为空
int isEmpty(HashTable* table);

// 8. 打印哈希表
void printHashTable(HashTable* table);

// 9. 清空哈希表
void clearHashTable(HashTable* table);

// 10. 获取当前负载因子
double getLoadFactor(HashTable* table);

源文件

cpp 复制代码
#include "666.h"

// 字符串哈希函数(djb2算法)
static unsigned long hashString(const char* str) {
    unsigned long hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;  // hash * 33 + c
    }
    return hash;
}

// 判断是否为质数
static int isPrime(int n) {
    if (n <= 1) return 0;
    if (n <= 3) return 1;
    if (n % 2 == 0 || n % 3 == 0) return 0;
    for (int i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) return 0;
    }
    return 1;
}

// 获取下一个质数
static int nextPrime(int n) {
    while (!isPrime(n)) {
        n++;
    }
    return n;
}

// 创建新节点
static HashNode* createNode(const char* key, int value) {
    HashNode* node = (HashNode*)malloc(sizeof(HashNode));
    if (node == nullptr) {
        printf("createNode err: 内存分配失败\n");
        return nullptr;
    }
    node->key = strdup(key);
    if (node->key == nullptr) {
        free(node);
        printf("createNode err: 字符串复制失败\n");
        return nullptr;
    }
    node->value = value;
    node->next = nullptr;
    return node;
}

// 释放节点
static void freeNode(HashNode* node) {
    if (node) {
        free(node->key);
        free(node);
    }
}

// 计算哈希索引
static int getHashIndex(HashTable* table, const char* key) {
    return hashString(key) % table->capacity;
}

// 重新哈希(扩容)
static void rehash(HashTable* table) {
    if (table == nullptr) return;
    int oldCapacity = table->capacity;
    int newCapacity = nextPrime(oldCapacity * 2);  // 容量翻倍并找质数
    printf("开始重新哈希: %d -> %d (负载因子: %.2f)\n",
        oldCapacity, newCapacity,
        (double)table->size / table->capacity);
    // 创建新的桶数组
    HashNode** newBuckets = (HashNode**)calloc(newCapacity, sizeof(HashNode*));
    if (newBuckets == nullptr) {
        printf("rehash err: 内存分配失败\n");
        return;
    }
    // 重新插入所有元素
    for (int i = 0; i < oldCapacity; i++) {
        HashNode* node = table->buckets[i];
        while (node) {
            HashNode* next = node->next;
            // 计算在新表中的索引
            int newIndex = hashString(node->key) % newCapacity;
            // 插入到新表
            node->next = newBuckets[newIndex];
            newBuckets[newIndex] = node;
            node = next;
        }
    }
    // 更新哈希表
    free(table->buckets);
    table->buckets = newBuckets;
    table->capacity = newCapacity;
    printf("重新哈希完成\n");
}

// 检查是否需要扩容
static void checkResize(HashTable* table) {
    if (table == nullptr) return;
    double currentLoad = (double)table->size / table->capacity;
    if (currentLoad >= table->load_factor) {
        rehash(table);
    }
}


// 1. 创建哈希表
HashTable* createHashTable() {
    HashTable* table = (HashTable*)malloc(sizeof(HashTable));
    if (table == nullptr) {
        printf("createHashTable err: 内存分配失败\n");
        return nullptr;
    }
    table->capacity = nextPrime(INITIAL_CAPACITY);
    table->size = 0;
    table->load_factor = LOAD_FACTOR_THRESHOLD;
    table->buckets = (HashNode**)calloc(table->capacity, sizeof(HashNode*));
    if (table->buckets == nullptr) {
        free(table);
        printf("createHashTable err: 桶数组分配失败\n");
        return nullptr;
    }
    printf("创建哈希表成功,容量: %d\n", table->capacity);
    return table;
}

// 2. 销毁哈希表
void destroyHashTable(HashTable* table) {
    if (table == nullptr) return;
    clearHashTable(table);
    if (table->buckets) {
        free(table->buckets);
    }
    free(table);
    printf("哈希表已销毁\n");
}

// 3. 插入键值对
int hashInsert(HashTable* table, const char* key, int value) {
    if (table == nullptr || key == nullptr) return 0;
    // 检查是否需要扩容
    checkResize(table);
    int index = getHashIndex(table, key);
    HashNode* head = table->buckets[index];
    // 检查键是否已存在
    HashNode* current = head;
    while (current) {
        if (strcmp(current->key, key) == 0) {
            // 键已存在,更新值
            printf("更新键: %s (旧值: %d -> 新值: %d)\n",
                key, current->value, value);
            current->value = value;
            return 1;
        }
        current = current->next;
    }
    // 键不存在,创建新节点
    HashNode* newNode = createNode(key, value);
    if (newNode == nullptr) {
        return 0;
    }
    // 插入到链表头部
    newNode->next = table->buckets[index];
    table->buckets[index] = newNode;
    table->size++;
    printf("插入键值对: [%s: %d] (索引: %d)\n", key, value, index);
    return 1;
}

// 4. 查找键对应的值
int* hashSearch(HashTable* table, const char* key) {
    if (table == nullptr || key == nullptr) return nullptr;
    int index = getHashIndex(table, key);
    HashNode* current = table->buckets[index];
    while (current) {
        if (strcmp(current->key, key) == 0) {
            return &current->value;
        }
        current = current->next;
    }
    return nullptr;
}

// 5. 删除键值对
int hashDelete(HashTable* table, const char* key) {
    if (table == nullptr || key == nullptr) return 0;
    int index = getHashIndex(table, key);
    HashNode* current = table->buckets[index];
    HashNode* prev = nullptr;
    while (current) {
        if (strcmp(current->key, key) == 0) {
            // 找到要删除的节点
            if (prev) {
                prev->next = current->next;
            }
            else {
                // 删除的是头节点
                table->buckets[index] = current->next;
            }
            printf("删除键值对: [%s: %d]\n", key, current->value);
            freeNode(current);
            table->size--;
            return 1;
        }
        prev = current;
        current = current->next;
    }
    printf("删除失败: 键 %s 不存在\n", key);
    return 0;
}

// 6. 获取哈希表大小
int getSize(HashTable* table) {
    if (table == nullptr) return 0;
    return table->size;
}

// 7. 判断哈希表是否为空
int isEmpty(HashTable* table) {
    if (table == nullptr) return 1;
    return table->size == 0;
}

// 8. 打印哈希表
void printHashTable(HashTable* table) {
    if (table == nullptr) {
        printf("哈希表为空指针\n");
        return;
    }
    if (isEmpty(table)) {
        printf("哈希表为空\n");
        return;
    }
    printf("\n========== 哈希表内容 ==========\n");
    for (int i = 0; i < table->capacity; i++) {
        printf("桶[%3d]: ", i);
        HashNode* current = table->buckets[i];
        if (current == nullptr) {
            printf("(空)");
        }
        else {
            while (current) {
                printf("[%s:%d]", current->key, current->value);
                if (current->next) {
                    printf(" -> ");
                }
                current = current->next;
            }
        }
        printf("\n");
    }
    printf("================================\n");
}

// 9. 清空哈希表
void clearHashTable(HashTable* table) {
    if (table == nullptr) return;
    for (int i = 0; i < table->capacity; i++) {
        HashNode* current = table->buckets[i];
        while (current) {
            HashNode* next = current->next;
            freeNode(current);
            current = next;
        }
        table->buckets[i] = nullptr;
    }
    table->size = 0;
    printf("哈希表已清空\n");
}

// 10. 获取当前负载因子
double getLoadFactor(HashTable* table) {
    if (table == nullptr || table->capacity == 0) return 0.0;
    return (double)table->size / table->capacity;
}
相关推荐
液态不合群2 小时前
链表的基本操作,用链表实现线性表
数据结构·链表
_OP_CHEN2 小时前
【C++数据结构进阶】从 Redis 底层到手写实现!跳表(Skiplist)全解析:手把手带你吃透 O (logN) 查找的神级结构!
数据结构·数据库·c++·redis·面试·力扣·跳表
名誉寒冰2 小时前
Redis 常用数据结构与实战避坑指南
数据结构·数据库·redis
girl-07263 小时前
2025.12.26代码分析
数据结构·算法
蒙奇D索大3 小时前
【数据结构】排序算法精讲|折半插入排序全解:高效优化、性能对比、实战剖析
数据结构·学习·考研·算法·排序算法·改行学it
聆风吟º4 小时前
【数据结构手札】顺序表实战指南(四):头插 | 头删
数据结构·顺序表·头插·头删
zore_c4 小时前
【C语言】排序算法——快速排序详解(含多种变式)!!!
c语言·数据结构·笔记·算法·排序算法·深度优先·推荐算法
客梦4 小时前
数据结构--哈希表
数据结构·笔记