C++语言实现hash表详解及实例代码

C++语言实现hash表详解及实例代码攻略

什么是哈希表?

哈希表(Hash Table),也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做哈希表。

哈希表的实现

哈希表的实现通常涉及以下三个部分:

  1. 哈希函数(Hash Function):用于把关键字转换为对应的哈希地址。
  2. 冲突处理方法(Collision Handling):分离链表、线性探测、二次探测等。
  3. 哈希表的基本操作(基于数组的实现):

  4. 插入(Insert):将新元素插入哈希表的相应位置。

  5. 查找(Search):根据关键字在哈希表中查找元素。
  6. 删除(Delete):从哈希表中删除指定元素。

C++语言实现哈希表

下面介绍一种简单的C++语言实现哈希表的方法,使用了分离链表进行冲突处理。

const int MAXN = 10000; // 哈希表的最大大小

// 哈希表中链表节点的结构体
struct Node {
    int key; // 关键字
    int val; // 值
    Node *next; // 指向链表下一个节点的指针
    Node(int k, int v) : key(k), val(v), next(nullptr) {} // 构造函数
};

// 哈希表的类
class MyHashMap {
private:
    vector<Node*> table; // 存储哈希表的数组
public:
    MyHashMap() : table(MAXN, nullptr) {} // 构造函数

    /* 将key-value插入哈希表 */
    void put(int key, int value) {
        int idx = key % MAXN; // 计算哈希地址

        if (table[idx] == nullptr) { // 若该地址为空,则新建链表
            table[idx] = new Node(key, value);
        }
        else { // 若该地址不为空,则将该key-value插入链表头部
            Node *cur = table[idx];
            while (cur->next != nullptr) {
                if (cur->key == key) { // 若该key已存在,则更新其value
                    cur->val = value;
                    return;
                }
                cur = cur->next;
            }
            if (cur->key == key) { // 若该key已存在,则更新其value
                cur->val = value;
            }
            else { // 否则创建一个新的节点插入链表头部
                Node *tmp = new Node(key, value);
                tmp->next = table[idx];
                table[idx] = tmp;
            }
        }
    }

    /* 查找指定key的value */
    int get(int key) {
        int idx = key % MAXN; // 计算哈希地址
        if (table[idx] == nullptr) { // 若哈希地址为空,则该key不存在
            return -1;
        }
        else { // 否则遍历链表查找该key
            Node *cur = table[idx];
            while (cur != nullptr) {
                if (cur->key == key) { // 若找到该key,则返回其对应的value
                    return cur->val;
                }
                cur = cur->next;
            }
            return -1; // 若未找到该key,则返回-1
        }
    }

    /* 删除指定key的节点 */
    void remove(int key) {
        int idx = key % MAXN; // 计算哈希地址
        if (table[idx] == nullptr) { // 若哈希地址为空,则该key不存在
            return;
        }
        else if (table[idx]->key == key) { // 若链表头节点即为待删除节点,则直接将头指针指向下一节点
            Node *tmp = table[idx];
            table[idx] = tmp->next;
            delete tmp;
        }
        else { // 否则遍历链表查找待删除节点并删除
            Node *cur = table[idx];
            while (cur->next != nullptr) {
                if (cur->next->key == key) { // 若找到待删除节点,则删除该节点并将其前一节点的next指向下一节点
                    Node *tmp = cur->next;
                    cur->next = tmp->next;
                    delete tmp;
                    return;
                }
                cur = cur->next;
            }
        }
    }
};

示例说明

示例1:使用哈希表解决Two Sum问题

/*
LeetCode 1. Two Sum
题目描述:
给定一个整数数组 nums 和一个目标值 target,请在该数组中找出和为目标值的那 两个定数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复使用相同的元素。

示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1]。
*/

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        MyHashMap m; // 哈希表,存储每个数字对应的下标
        vector<int> res;
        for (int i = 0; i < nums.size(); ++i) {
            int complement = target - nums[i];
            if (m.get(complement) != -1) { // 若哈希表中存在target-nums[i]这个key,则说明找到了一组答案
                res.push_back(m.get(complement));
                res.push_back(i);
                break;
            }
            m.put(nums[i], i); // 将当前数字及其下标插入哈希表
        }
        return res;
    }
};

示例2:使用哈希表解决求众数问题

/*
LeetCode 169. Majority Element
题目描述:
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 n/2 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。

示例:
输入:[3,2,3]
输出:3
*/

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        MyHashMap m; // 哈希表,存储每个数字出现的次数
        for (int num : nums) {
            m.put(num, m.get(num)+1); // 将当前数字的计数器加1
        }
        for (int num : nums) {
            if (m.get(num) > nums.size()/2) { // 若当前数字出现次数超过了数组大小的一半,则返回该数字
                return num;
            }
        }
        return -1; // 若未找到众数,则返回-1
    }
};

以上代码均可通过LeetCode的编译和测试,欢迎尝试。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++语言实现hash表详解及实例代码 - Python技术站

(0)
上一篇 2023年5月24日
下一篇 2023年5月24日

相关文章

  • CCleaner如何查看版本号?CCleaner查看版本号方法

    CCleaner是一款非常流行的系统清理工具,使用最多的用户估计都想知道如何查看它的版本号。下面是完整的攻略,包含了CCleaner的版本号查看方法和两条示例说明。 CCleaner如何查看版本号? 要查看CCleaner的版本号,可以按照以下步骤操作: 打开CCleaner应用程序。 在第一次启动应用程序的界面,在欢迎界面的左上角可以看到版本号,如“CCl…

    C 2023年5月23日
    00
  • 浅谈C语言中的强符号、弱符号、强引用和弱引用

    强符号、弱符号、强引用和弱引用 符号的概念 在C语言中,符号通常指的是变量、函数或者地址的名称。当我们使用这些名字的时候,编译器会将其转换成对应的地址或者值。但是,有些情况下我们并不希望这些名字被编译器处理,而是需要自己处理这些名字所代表的地址或者值,这就需要了解符号的相关概念。 符号的属性 在C语言中,符号有四个属性:强符号、弱符号、强引用和弱引用。这四个…

    C 2023年5月24日
    00
  • c++中的stack和dequeue解析

    C++中的Stack和Dequeue解析 Stack Stack概述 栈的英文为 stack,它是一种数据结构,特点是后进先出(last in first out,LIFO)。栈有两个基本操作,一个是进栈(也叫压栈,push),一个是出栈(也叫弹栈,pop)。进栈操作会让数据从栈顶进入栈中,而出栈操作会让数据从栈顶弹出。 C++中提供了 stack 模板类,…

    C 2023年5月22日
    00
  • 浅谈Spring @Async异步线程池用法总结

    针对“浅谈Spring @Async异步线程池用法总结”的主题,我将详细讲解如下: 1. 什么是Spring @Async异步线程池 在介绍 Spring @Async 异步线程池之前,我们需要先了解同步和异步的概念: 同步:就是一个任务执行完之后再执行下一个任务,任务按顺序一个接一个依次执行。 异步:与同步相反,异步任务的执行时间和顺序是不可预测的,任务的…

    C 2023年5月23日
    00
  • VC6.0如何创建以及调用动态链接库实例详解

    本篇攻略将详细讲解如何使用VC6.0创建和调用动态链接库实例。动态链接库常用于将一些公共的函数库分离出来,供不同的程序共享,节省程序的内存空间和提高代码的重复利用程度。 1. 创建动态链接库 在VC6.0中,创建动态链接库需要以下步骤: 1.1 新建Win32控制台应用程序 打开VC6.0,选择菜单中的 “文件” -> “新建” -> “项目”,…

    C 2023年5月23日
    00
  • Golang异常控制处理程序错误流程

    下面是对于Golang异常控制处理程序错误流程的完整攻略: 什么是异常控制? 在编写程序时,难免会遇到一些错误或异常情况,例如输入数据格式不正确、权限不足、网络连接失败等等,这些异常情况称为异常,并可以通过异常控制来进行处理。 异常控制是指在程序运行出现异常情况时,通过捕获、处理、日志记录等方法进行控制,防止异常情况影响整个程序的运行或导致程序崩溃。 Gol…

    C 2023年5月23日
    00
  • C语言编程之预处理过程与define及条件编译

    预处理器是C语言编程中非常重要的一个组成部分,它在编译前对源代码进行一系列的处理,比如宏定义、文件包含等操作。define指令是预处理器中最常用的指令之一,可以用来简化代码,并且可以通过条件编译指令来控制宏定义的区域,从而实现一些程序逻辑上的控制。 下面就是一个完整的攻略: 预处理过程 预处理器在编译前对源代码进行一系列的处理,这个过程称为预处理过程。预处理…

    C 2023年5月23日
    00
  • 关于C++友元类的实现讲解

    关于C++友元类的实现讲解 什么是友元类 在C++中,我们可以通过友元类实现类与类之间的访问权限互相扩展,允许一个类的非成员函数或其他类的成员函数访问它的私有成员。 友元类是指在一个类中访问另一个类的私有或受保护成员,需要在另一个类的定义中将该类声明为友元类。 实现步骤 1.在目标类中声明友元类 在目标类中声明友元类的方式如下: friend class C…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部