返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言手撕一个Hash表(HashTable)实例代码
  • 186
分享到

C语言手撕一个Hash表(HashTable)实例代码

c语言hashc语言实现哈希表c语言hash表的实现 2023-03-24 14:03:33 186人浏览 独家记忆
摘要

目录什么是Hash Table散列函数散列冲突开放寻址法链表法装载因子代码总结什么是Hash Table 散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种

什么是Hash Table

散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。可以说,如果没有数组,就没有散列表。

散列函数

散列函数是将我们想插入的节点散列成一个数值的函数。它是一个函数。我们可以把它定义成 hash(key),要想找到一个不同的 key 对应的散列值都不一样的散列函数,几乎是不可能的。即便像业界著名的MD5、SHA、CRC等哈希算法,也无法完全避免这种散列冲突。而且,因为数组的存储空间有限,也会加大散列冲突的概率。

所以我们几乎无法找到一个完美的无冲突的散列函数,即便能找到,付出的时间成本、计算成本也是很大的,所以针对散列冲突问题,我们需要通过其他途径来解决。

散列冲突

开放寻址法

开放寻址法的核心思想是,如果出现了散列冲突,我们就重新探测一个空闲位置,将其插入。那如何重新探测新的位置呢?我先讲一个比较简单的探测方法,线性探测(Linear Probing)。当我们往散列表中插入数据时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。

链表法

链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。我们来看这个图,在散列表中,每个“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中。

HashMap 底层采用链表法来解决冲突。即使负载因子和散列函数设计得再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响 HashMap 的性能。

于是,在 jdk1.8 版本中,为了对 HashMap 做进一步优化,我们引入了红黑树。而当链表长度太长(默认超过 8)时,链表就转换为红黑树。我们可以利用红黑树快速增删改查的特点,提高 HashMap 的性能。当红黑树结点个数少于 8 个的时候,又会将红黑树转化为链表。因为在数据量较小的情况下,红黑树要维护平衡,比起链表来,性能上的优势并不明显。

装载因子

装载因子越大,说明散列表中的元素越多,空闲位置越少,散列冲突的概率就越大。不仅插入数据的过程要多次寻址或者拉很长的链,查找的过程也会因此变得很慢。

最大装载因子默认是 0.75,当 HashMap 中元素个数超过 0.75*capacity(capacity 表示散列表的容量)的时候,就会启动扩容,每次扩容都会扩容为原来的两倍大小。

代码

这里我们给出C语言的HashTable代码,我们使用的是链表法,而且也没有在链表过长的时候将其转化为红黑树,只是单纯的链表。

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

typedef struct node {
    int key;
    int val;
    struct Node *next;
} Node;

typedef struct HashMap {
    int size;
    int count;
    struct Node **hashmap;
} HashMap;

// 定义哈希函数
unsigned int hash(int n) {
    return (unsigned int) n;
}

// 创建一个节点
Node *creatNode(int key, int val) {
    Node *node = (Node *) malloc(sizeof(Node));
    node->key = key;
    node->val = val;
    node->next = NULL;
    return node;
}

// 创建一个hash表
HashMap *createHashMap() {
    HashMap *H = (HashMap *) malloc(sizeof(HashMap));
    H->size = 8;
    H->count = 0;
    H->hashmap = (Node **) calloc(H->size, sizeof(Node *));
    return H;
}

// 扩容,以2倍的形式扩容
static void extend(HashMap *map) {
    int newsize = map->size * 2;
    Node **newhashmap = (Node **) calloc(newsize, sizeof(Node *));
    for (int i = 0; i < map->size; i++) {
        Node *node = map->hashmap[i];
        Node *head1 = (Node *) malloc(sizeof(Node));
        Node *head2 = (Node *) malloc(sizeof(Node));
        Node *temp1 = head1;
        Node *temp2 = head2;
        while (node) {
            if (hash(node->key) % newsize != i) {
                temp2->next = node;
                temp2 = temp2->next;
            } else {
                temp1->next = node;
                temp1 = temp1->next;
            }
            node = node->next;
        }
        temp1->next = NULL;
        temp2->next = NULL;
        newhashmap[i] = head1->next;
        newhashmap[i + map->size] = head2->next;
        free(head1);
        free(head2);
    }
    map->size = newsize;
    free(map->hashmap);
    map->hashmap = newhashmap;
}

// 插入某个结点
bool insert(HashMap *map, int key, int val) {
    int hash_key = hash(key) % map->size;
    // 要插入的哈希值未产生碰撞
    if (map->hashmap[hash_key] == NULL) {
        map->count++;
        map->hashmap[hash_key] = creatNode(key, val);
    } else {
        Node *head = map->hashmap[hash_key];
        if (head->key == key) return false;
        while (head->next && head->next->key != key) head = head->next;
        if (head->next == NULL) {
            map->count++;
            head->next = creatNode(key, val);
        } else if (head->next->key == key) return false;
    }
	// 装载因子过高就开始扩容
    if (map->count / map->size > 0.75) extend(map);
    return true;
}

// 寻找某个结点
bool find(HashMap *map, int key, int *v) {
    int hash_key = hash(key) % map->size;
    if (map->hashmap[hash_key] == NULL) {
        return false;
    } else {
        Node *head = map->hashmap[hash_key];
        if (head->key == key) {
            *v = head->val;
            return true;
        }
        while (head->next && head->next->key != key) head = head->next;
        if (head->next == NULL) return false;
        if (head->next->key == key) {
            *v = head->next->val;
            return true;
        }
    }
    return false;
}

// 删除某个结点
void delete(HashMap *map, int key) {
    int hash_key = hash(key) % map->size;
    if (map->hashmap[hash_key] == NULL) return;
    Node *head = map->hashmap[hash_key];
    if (head->key == key) {
        map->hashmap[hash_key] = head->next;
        map->count--;
        free(head);
        return;
    }
    while (head->next && head->next->key != key) head = head->next;
    if (head->next == NULL) return;
    if (head->next->key == key) {
        Node *temp = head->next;
        head->next = head->next->next;
        map->count--;
        free(temp);
    }
    return;
}


int main() {
    HashMap *H = createHashMap();
    for (int i = 0; i < 1024; i++) {
        insert(H, i, i);
    }
    printf("H size is %d\n",H->size);
    printf("H count is %d\n",H->count);
    delete(H, 0);
    int v = 0;
    if (find(H, 0, &v)) printf("%d\n", v);
    else printf("not find \n");
    if (find(H, 4, &v)) printf("%d\n", v);
    else printf("not find \n");
    return 0;
}

总结

到此这篇关于C语言手撕一个Hash表(HashTable)的文章就介绍到这了,更多相关C语言Hash表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言手撕一个Hash表(HashTable)实例代码

本文链接: https://lsjlt.com/news/201199.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言手撕一个Hash表(HashTable)实例代码
    目录什么是Hash Table散列函数散列冲突开放寻址法链表法装载因子代码总结什么是Hash Table 散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种...
    99+
    2023-03-24
    c语言hash c语言实现哈希表 c语言hash表的实现
  • C语言动态顺序表实例代码
    目录顺序表概念:一.准备工作二、顺序表的基本操作 1.顺序表的初始化函数2.尾插函数(在尾部插入数据)3.头插函数(在数组头部插入数据) 4.尾删函数5.头删函数6.在第pos的位置...
    99+
    2024-04-02
  • C语言实现动态链表的示例代码
    目录结构体定义已经函数声明函数实现创建一个链表判断链表是否为空获得链表中节点的个数在某个特定的位置插入一个元素获得指定下标的节点的元素删除一个节点链表逆序链表的清空链表的销毁链表的遍...
    99+
    2024-04-02
  • C语言实现手写红黑树的示例代码
    目录前沿红黑树代码测试前沿 写C的红黑树前建议先看我博客这篇文章Java-红黑树 主要看原理 红黑树代码 #ifndef STUDY_RBTREE_H #define ...
    99+
    2024-04-02
  • C语言实现手写Map(数组+链表+红黑树)的示例代码
    目录要求结构红黑树和链表转换策略hash使用要求 需要准备数组集合(List) 数据结构 需要准备单向链表(Linked) 数据结构 需要准备红黑树(Rbtree)数据结构 需要准备...
    99+
    2024-04-02
  • C语言实现动态顺序表的示例代码
    目录顺序表概念及结构基本操作功能实现程序运行顺序表概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 分...
    99+
    2022-11-13
    C语言 动态顺序表 C语言 顺序表
  • C语言手写集合List的示例代码
    目录前沿定义结构创建List扩容创建数据节点给集合添加值删除集合内指定的值删除集合内指定下标的值打印集合迭代器查询指定元素的下标(第一个)末尾查询指定元素下标(第一个)判断数组是否有...
    99+
    2024-04-02
  • C语言实现手写Map(全功能)的示例代码
    目录为啥需要Map结构主流Map结构数组+链表的Map结构hash函数创建Map集合扩容基数扩容Map集合给Map集合添加元素打印Map集合获取Map集合中的指定元素判断键是否存在判...
    99+
    2024-04-02
  • C语言实现三子棋实例代码
    我是半自主的完成了这个程序,看了B站鹏哥视频并仔细思索后才做出来的,我没有完全采用他的方法,导致程序还有一些不足之处,还请各位大佬指出。 首先,我将该程序的实现分为3个板块,main...
    99+
    2024-04-02
  • C语言实现栈的示例代码
    目录一、了解栈的结构特点二、具体实现补充 栈的用处一、了解栈的结构特点 栈是一种特殊的线性表,只允许从一端进出数据,称为后进先出,先进后出。 压栈:栈的插入操作叫做进栈/压...
    99+
    2024-04-02
  • C语言实现无头单向链表的示例代码
    目录一、易错的接口实现 1.1 新节点开辟函数 1.2 尾插 1.3 尾删 二、常见简单接口 2.1 打印链表 2.2 节点计数器 2.3 判断是否为空链表 2.4 通过值查找节点 ...
    99+
    2024-04-02
  • C语言动态顺序表实例代码怎么编写
    这篇文章给大家介绍C语言动态顺序表实例代码怎么编写,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。顺序表概念:        顺序表是用一段物理地址连续的存储单元依次存储数据元素的...
    99+
    2023-06-22
  • 用c语言实现一个电话薄(附完整代码)
    先看一下这个小程序的效果 这里我为了演示方便,把人数固定为3个; 人数都是可以自定义的; 下面是这个简单的代码: #include<stdio.h> typedef s...
    99+
    2024-04-02
  • C语言制作表白神器的示例代码
    目录程序说明操作说明程序效果完整源码程序说明 这是一个表白神器,可以自己替换上图片识别。 1.程序默认是识别 640×480 尺寸,可以自己调整。 2.也有现成的程序可以...
    99+
    2023-03-07
    C语言制作表白神器 C语言表白神器 C语言表白
  • C语言打印正方形实例代码
    目录题目描述输入输出样例输入样例输出题目描述 由火柴棍组成的一个n×n的正方形,按从上到下,从左到右的顺序给火柴棍编号,从1开始,比如下图中,一共有24根火柴棍。 问去掉若干个火柴棍...
    99+
    2024-04-02
  • C语言实现链表与文件存取的示例代码
    目录此处为main函数的内容一、输入数据到链表中二、把链表数据存入文件三、输出文件完整代码本程序主要功能是建立链表,然后把链表数据存储到文件中,然后把文件数据存储到数组中并输出。 不...
    99+
    2024-04-02
  • C++ 实现一个复数类的实例代码
    要求 实现⼀个复数类 Complex 。 Complex 类包括两个 double 类型的成员 real 和 image ,分别表示复数的实部和虚部。 对 Comple...
    99+
    2024-04-02
  • C语言实现烟花表白程序代码
    目录效果图烟花爆炸效果思路代码素材总结效果图 烟花爆炸效果思路 不能直接把烟花图片贴到窗口中,需要把烟花的像素点保存到二维数组中,以相同的半径大小把烟花输出到窗口中爆炸的位置,r...
    99+
    2024-04-02
  • C语言如何实现一个链表队列
    本篇内容主要讲解“C语言如何实现一个链表队列”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言如何实现一个链表队列”吧!C语言数据结构链表队列的实现1.写在前面  队列是一种和栈相反的,遵循先...
    99+
    2023-06-16
  • C语言实现线性动态(单向)链表的示例代码
    目录什么是链表为什么不用结构体数组链表的操作创建表删除元素插入元素代码及运行结果什么是链表 链表是数据结构里面的一种,线性链表是链表的一种,线性链表的延伸有双向链表和环形链表。在编程...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作