怎么使用C语言实现Hash表

其他教程   发布日期:2023年11月15日   浏览次数:698

这篇“怎么使用C语言实现Hash表”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用C语言实现Hash表”文章吧。

    什么是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代码,我们使用的是链表法,而且也没有在链表过长的时候将其转化为红黑树,只是单纯的链表。

    1. #include <stdlib.h>
    2. #include <stdio.h>
    3. #include <stdbool.h>
    4. typedef struct Node {
    5. int key;
    6. int val;
    7. struct Node *next;
    8. } Node;
    9. typedef struct HashMap {
    10. int size;
    11. int count;
    12. struct Node **hashmap;
    13. } HashMap;
    14. // 定义哈希函数
    15. unsigned int hash(int n) {
    16. return (unsigned int) n;
    17. }
    18. // 创建一个节点
    19. Node *creatNode(int key, int val) {
    20. Node *node = (Node *) malloc(sizeof(Node));
    21. node->key = key;
    22. node->val = val;
    23. node->next = NULL;
    24. return node;
    25. }
    26. // 创建一个hash表
    27. HashMap *createHashMap() {
    28. HashMap *H = (HashMap *) malloc(sizeof(HashMap));
    29. H->size = 8;
    30. H->count = 0;
    31. H->hashmap = (Node **) calloc(H->size, sizeof(Node *));
    32. return H;
    33. }
    34. // 扩容,以2倍的形式扩容
    35. static void extend(HashMap *map) {
    36. int newsize = map->size * 2;
    37. Node **newhashmap = (Node **) calloc(newsize, sizeof(Node *));
    38. for (int i = 0; i < map->size; i++) {
    39. Node *node = map->hashmap[i];
    40. Node *head1 = (Node *) malloc(sizeof(Node));
    41. Node *head2 = (Node *) malloc(sizeof(Node));
    42. Node *temp1 = head1;
    43. Node *temp2 = head2;
    44. while (node) {
    45. if (hash(node->key) % newsize != i) {
    46. temp2->next = node;
    47. temp2 = temp2->next;
    48. } else {
    49. temp1->next = node;
    50. temp1 = temp1->next;
    51. }
    52. node = node->next;
    53. }
    54. temp1->next = NULL;
    55. temp2->next = NULL;
    56. newhashmap[i] = head1->next;
    57. newhashmap[i + map->size] = head2->next;
    58. free(head1);
    59. free(head2);
    60. }
    61. map->size = newsize;
    62. free(map->hashmap);
    63. map->hashmap = newhashmap;
    64. }
    65. // 插入某个结点
    66. bool insert(HashMap *map, int key, int val) {
    67. int hash_key = hash(key) % map->size;
    68. // 要插入的哈希值未产生碰撞
    69. if (map->hashmap[hash_key] == NULL) {
    70. map->count++;
    71. map->hashmap[hash_key] = creatNode(key, val);
    72. } else {
    73. Node *head = map->hashmap[hash_key];
    74. if (head->key == key) return false;
    75. while (head->next && head->next->key != key) head = head->next;
    76. if (head->next == NULL) {
    77. map->count++;
    78. head->next = creatNode(key, val);
    79. } else if (head->next->key == key) return false;
    80. }
    81. // 装载因子过高就开始扩容
    82. if (map->count / map->size > 0.75) extend(map);
    83. return true;
    84. }
    85. // 寻找某个结点
    86. bool find(HashMap *map, int key, int *v) {
    87. int hash_key = hash(key) % map->size;
    88. if (map->hashmap[hash_key] == NULL) {
    89. return false;
    90. } else {
    91. Node *head = map->hashmap[hash_key];
    92. if (head->key == key) {
    93. *v = head->val;
    94. return true;
    95. }
    96. while (head->next && head->next->key != key) head = head->next;
    97. if (head->next == NULL) return false;
    98. if (head->next->key == key) {
    99. *v = head->next->val;
    100. return true;
    101. }
    102. }
    103. return false;
    104. }
    105. // 删除某个结点
    106. void delete(HashMap *map, int key) {
    107. int hash_key = hash(key) % map->size;
    108. if (map->hashmap[hash_key] == NULL) return;
    109. Node *head = map->hashmap[hash_key];
    110. if (head->key == key) {
    111. map->hashmap[hash_key] = head->next;
    112. map->count--;
    113. free(head);
    114. return;
    115. }
    116. while (head->next && head->next->key != key) head = head->next;
    117. if (head->next == NULL) return;
    118. if (head->next->key == key) {
    119. Node *temp = head->next;
    120. head->next = head->next->next;
    121. map->count--;
    122. free(temp);
    123. }
    124. return;
    125. }
    126. int main() {
    127. HashMap *H = createHashMap();
    128. for (int i = 0; i < 1024; i++) {
    129. insert(H, i, i);
    130. }
    131. printf("H size is %d
    132. ",H->size);
    133. printf("H count is %d
    134. ",H->count);
    135. delete(H, 0);
    136. int v = 0;
    137. if (find(H, 0, &v)) printf("%d
    138. ", v);
    139. else printf("not find
    140. ");
    141. if (find(H, 4, &v)) printf("%d
    142. ", v);
    143. else printf("not find
    144. ");
    145. return 0;
    146. }

    以上就是怎么使用C语言实现Hash表的详细内容,更多关于怎么使用C语言实现Hash表的资料请关注九品源码其它相关文章!