当前位置:首页 > 其他 > 手把手教你实现哈希表:从代码到原理的新手友好指南

手把手教你实现哈希表:从代码到原理的新手友好指南

3个月前 (06-28)

一、简介和应用

哈希表(Hash Table)是一种高效的数据结构,通过哈希函数将键(Key)映射到存储位置,实现O(1)时间复杂度的查找、插入和删除操作。它广泛应用于缓存系统、数据库索引、字典查询等场景。例如,在编程中需要快速根据用户ID查找信息时,哈希表能显著提升效率。本文将通过手写的C++哈希表代码,带您从零开始理解其实现原理。

二、特点和注意事项

1. 特点:

    快速访问:通过哈希函数直接定位数据,避免线性查找

    动态扩容:本例中默认大小为1000,可自定义大小(但需注意扩容策略未在代码中实现)。

    冲突解决:使用链地址法(即每个哈希桶存储链表),解决不同键映射到同一地址的问题。

2. 注意事项:

    哈希函数设计:需尽量均匀分布,减少冲突(本例用取模运算,简单但可能不均)。

    内存管理:代码中使用了new动态分配节点,但未显式释放,实际应用需考虑内存泄漏风险。

    链表长度:若冲突过多,链表过长可能导致性能下降,需优化哈希函数或改用其他策略。

三、实现步骤

1. 定义数据结构:

    使用pairs结构体存储键值对(key和val)。

    listnode节点包含值及指向下一个节点的指针,形成链表。

    hash_map类维护哈希表数组(存储链表头指针)和表大小。

2. 构造函数:

    默认或传入指定大小初始化数组,并全部置空。

3. 核心方法:

    ins():计算键的哈希地址,新建节点插入链表末尾(处理头节点为空和冲突情况)。

    del():定位键的哈希地址,遍历链表删除对应节点(需判断头节点是否为目标)。

    find():查找并返回指定键的值,未找到则返回-1。

    print():遍历整个哈希表输出所有键值对。

4. 冲突处理:通过链表串联同一地址下的多个元素,避免数据覆盖。

四、代码和注释

#include <iostream>
using namespace std;

// 键值对结构体
struct pairs {
    int key;      // 键
    int val;      // 值
};

// 链表节点(存储键值对)
template<typename T>
struct listnode {
    T val;        // 节点值
    listnode* next = nullptr;  // 指向下一个节点

    // 构造节点
    listnode() {}                // 空构造
    listnode(T v) { val = v; }    // 传入值构造
};

// 哈希表类
class hash_map {
private:
    listnode<pairs>** map;        // 存储链表头指针的数组
    int size;                    // 哈希表大小

public:
    // 默认构造函数(初始化大小为1000)
    hash_map() {
        size = 1000;
        map = new listnode<pairs>*[size];   // 动态分配数组
        for (int i = 0; i < size; i++) {   // 初始化所有位置为空
            map[i] = nullptr;
        }
    }

    // 指定大小构造函数
    hash_map(int size) {
        this->size = size;                // 使用传入的大小
        map = new listnode<pairs>*[size];
        for (int i = 0; i < size; i++) {
            map[i] = nullptr;
        }
    }

    // 插入键值对
    void ins(pairs pair) {
        int address = pair.key % size;     // 哈希函数:取模定位地址
        listnode<pairs>* newnode = new listnode<pairs>(pair); // 创建新节点

        listnode<pairs>* tmp = map[address];  // 当前地址的链表头
        if (!tmp) {                          // 若头节点为空,直接插入
            map[address] = newnode;
            return;
        }
        while (tmp->next) {                  // 否则遍历到链表末尾
            tmp = tmp->next;
        }
        tmp->next = newnode;                 // 插入末尾
    }

    // 删除指定键
    void del(int key) {
        int address = key % size;            // 定位地址
        listnode<pairs>* tmp = map[address];
        if (tmp->val.key == key) {           // 若头节点就是目标
            map[address] = map[address]->next;  // 删除头节点
            return;
        }
        while (tmp->next->val.key!= key) {  // 遍历查找目标节点
            tmp = tmp->next;
        }
        tmp->next = tmp->next->next;        // 删除目标节点
    }

    // 查找指定键的值
    int find(int key) {
        int address = key % size;
        listnode<pairs>* tmp = map[address];
        while (tmp && tmp->val.key!= key) {  // 遍历查找
            tmp = tmp->next;
        }
        if (!tmp) return -1;                 // 未找到返回-1
        return tmp->val.val;                  // 返回值
    }

    // 打印所有键值对
    void print() {
        for (int i = 0; i < size; i++) {
            listnode<pairs>* tmp = map[i];
            while (tmp) {                    // 遍历当前地址的链表
                cout << tmp->val.key << ":" << tmp->val.val << " ";
                tmp = tmp->next;
            }
            cout << endl;                    // 换行分隔不同地址
        }
        cout << endl;
    }
};

五、总结

通过本文的代码与注释,您已初步掌握了哈希表的核心实现逻辑:利用哈希函数映射键到地址,通过链表解决冲突,从而实现高效的增删查操作。实际应用中,需进一步优化哈希函数(如使用更均匀的算法)和考虑内存管理。建议新手从简单示例入手,逐步实践,理解数据结构背后的设计思想,为后续学习更复杂算法打下基础。


链接:哈希表实现指南:从原理到C++实践

原创内容 转载请注明出处

分享给朋友:

相关文章

力扣第92题:三步定位 精准反转链表指定区间

力扣第92题:三步定位 精准反转链表指定区间

题目解读给定一个单链表和两个整数left与right,要求将链表中从第left个节点到第right个节点的部分进行反转,而保持其他部分不变。例如,对于链表1→2→3→4→5,left=2,right=...

2025年GESP七级等价消除(洛谷P11965)代码解析与优化策略

2025年GESP七级等价消除(洛谷P11965)代码解析与优化策略

一、题目解读    2025年GESP七级考试中的“等价消除(洛谷P11965)”问题要求统计给定字符串中满足等价条件的子串数量。所谓“等价子串”,是指子串中所...

手搓双向链表代码全解析:从零开始实现双向链表数据结构(附注释与实战步骤)

一、简介和特点双向链表(Double Linked List)是一种基础的数据结构,它由多个节点组成,每个节点包含数据域、指向下一个节点的指针(next)和指向前一个节点的指针(last)。与单向链表...

力扣2588题解:利用前缀异或和与哈希表求解美丽子数组问题

力扣2588题解:利用前缀异或和与哈希表求解美丽子数组问题

一、题目解读力扣2588题要求计算给定数组中“美丽子数组”的数量。所谓“美丽子数组”,是指子数组的异或和为0。题目关键在于理解子数组异或和的性质,并通过高效算法统计符合条件的子数组数量。二、解题思路采...

洛谷P1102题解:利用哈希表优化的数对统计 C++代码解析

洛谷P1102题解:利用哈希表优化的数对统计 C++代码解析

一、题目解读洛谷P1102题要求处理一组整数数组与常数C,统计数组中是否存在元素A与B满足A+B=C。用户需输出满足条件的数对数量。题目关键在于快速判断是否存在互补元素,时间复杂度需优化以避免暴力遍历...

洛谷P3369题解:Treap动态数据结构详解与代码实现

洛谷P3369题解:Treap动态数据结构详解与代码实现

一、题目解读洛谷P3369题要求处理动态区间内的数据操作,涉及插入、删除及查询等。题目难点在于维护数据的动态平衡与高效查找,传统数据结构难以满足其性能需求。因此,需采用具有随机化平衡特性的Treap(...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。