笔者最近学习数据结构中的哈希表,并用C语言简单实现了。
当然源代码多有参考,此博客旨在交流心得
结构体说明如下:
typedef struct element
{
int key;
int value;
int hash;
}Element;
Element 代表每个储存单元的类型,是自定义的结构体。
value 是Element下储存的值(可以自己改为其他的)。
key 是用来构建哈希表的键值(是唯一不重复的值)
hash 是经哈希函数处理后得到的值 。
typedef struct Pnode
{
Element *data;
struct Pnode *next;
}Node;
Node 是单链表的节点。
存放有 Element 的值
和下一个节点地址的指针域next
typedef struct hash_table
{
int size;
int length;
struct Pnode *head;
}Hash_table;
Hash_table 是哈希表结构体。
存放有哈希表的大小size(这里与哈希函数有关,我的是,REMAINDER=11)和长度length(储存元素数量)。
head 域存储下图左边数组下标0的地址
注:拉链法解决冲突的方式就是将冲突的元素接在与之冲突的节点的后面,从而形成一个单链表。
部分函数说明(涉及到malloc函数使用和单链表的操作):
表头初始化:
Hash_table* Creat_Table(int table_size)
{
Hash_table *h = (Hash_table *)malloc(sizeof(Hash_table));
h->size = REMAINDER;
h->head = (Node *)malloc((h->size)*sizeof(Node));
h->length = 0;
int i = 0;
for(i=0 ; i<h->size ; i++)
{
h->head[i].next = NULL;
}
return h;
}
创建哈希表,并返回表头指针。
同时为表头下的head域开辟长度为十一的数组,并将其头地址赋给head。
同时初始化表,是大小设置为 REMAINDER 11 ,长度设置为零 , 并将head下的每个节点的next域赋值为空(防止因编译器的原因出错)。
插入函数:
其实传结构体地址更为方便(笔者偷懒就没实现了。。。)
void Insert(Hash_table *h , Element k)
{
Node * p = lookup(h,k.key);
if(!p)
{
Node *q = (Node *)malloc(sizeof(Node));
q->data = (Element *)malloc(sizeof(Element));
(q->data)->key = k.key;
(q->data)->value = k.value;
int position;
position = (q->data)->hash = hash(k.key);
q->next = h->head[position].next;
h->head[position].next = q;
h->length += 1;
return ;
}
else
{
printf("The keys is exist !\n");
return ;
}
}
首先查找所传入的值是否已在哈希表中(如果存在就退出)。
如果不存在:
开辟新的 Element 域 , 并将结构体赋值给他(没有传地址的锅,导致实现比较粗暴,读者可以自己尝试更简单的方法。)
并计算key所对应的hash值,并将节点赋到指定位置上(涉及单链表的操作)
注意,无论是否冲突,实现的方式是不变的(这也是拉链法的好处之一,插入很方便)
查找函数:
Node *lookup(Hash_table *h , int key)
{
int i;
i = hash(key);
Node * p = h->head[i].next;
while(p && key != p->data->key)
{
p = p->next;
}
return p;
}
先计算key值所对应的哈希值,并逐一检查所对应位置的单链表的节点,并返回其地址。
源代码如下:
#include<stdio.h>
#include<stdlib.h>
#define REMAINDER 11 // 通常是质数
typedef struct element
{
int key;
int value;
int hash;
}Element;
typedef struct Pnode
{
Element *data;
struct Pnode *next;
}Node;
typedef struct hash_table
{
int size;
int length;
struct Pnode *head;
}Hash_table;
int hash(int value)
{
return value%REMAINDER;
}
Hash_table* Creat_Table(int table_size)
{
Hash_table *h = (Hash_table *)malloc(sizeof(Hash_table));
h->size = REMAINDER;
h->head = (Node *)malloc((h->size)*sizeof(Node));
h->length = 0;
int i = 0;
for(i=0 ; i<h->size ; i++)
{
h->head[i].next = NULL;
}
return h;
}
Node *lookup(Hash_table *h , int key)
{
int i;
i = hash(key);
Node * p = h->head[i].next;
while(p && key != p->data->key)
{
p = p->next;
}
return p;
}
void Insert(Hash_table *h , Element k)
{
Node * p = lookup(h,k.key);
if(!p)
{
Node *q = (Node *)malloc(sizeof(Node));
q->data = (Element *)malloc(sizeof(Element));
(q->data)->key = k.key;
(q->data)->value = k.value;
int position;
position = (q->data)->hash = hash(k.key);
q->next = h->head[position].next;
h->head[position].next = q;
h->length += 1;
return ;
}
else
{
printf("The keys is exist !\n");
return ;
}
}
void Destroy_Table(Hash_table *h)
{
int i;
Node *p , *q;
for(i=0 ; i<h->size ; i++)
{
p = h->head[i].next;
while(p)
{
q=p->next;
free(p);
p=q;
}
}
free(h->head);
free(h);
}
void print_Table(Hash_table *h)
{
int i = 0;
for (i = 0; i < h->size ; i++)
{
Node * p = h->head[i].next;
while (p)
{
printf("[%d-%d] ",p->data->key, p->data->value);
p = p->next;
}
printf("NULL\n");
}
}
int main()
{
Element a[]={{12,1},{2,2},{31,3},{45,4},{8,5}};
int n = sizeof(a)/sizeof(Element);
Hash_table *h = Creat_Table(n);
int i = 0;
for(i = 0 ; i<n ; i++)
{
Insert(h,a[i]);
}
print_Table(h); // 打印哈希表
printf("%d\n\n", lookup(h,12)->data->key); //查找key值为12的Element
printf("%d\n",h->length); //打印哈希表的元素个数
Destroy_Table(h); // 摧毁哈希表
return 0;
}
源代码截图: