数据结构 哈希表
哈希表
哈希存储(散列存储)
将要存储数据的关键字和数据存储位置之间建立起对应的函数关系
当数据存储时,根据该关系映射数据的存储位置;
查找数据时,利用该函数关系
目的:为了快速检索数据

哈希冲突/哈希矛盾:key1!=key2 f(key1) == f(key2)
解决哈希冲突的方法:
开放定址法
不额外使用链表等外部空间,所有元素都存放在哈希表的数组本身中,通过“寻找下一个可以位置”来解决冲突
适合数据量小,占用内存小的数据

链地址法
将哈希值相同的元素,存储在同一个链表中,哈希表的每个位置对应一条链表
即哈希表本质是存放链表头指针的数组
原理:对关键字计算哈希地址,映射到哈希表数组的某一下标。
若该位置无元素,直接存入。
若已有元素(发送冲突),则把新结点追加到对应链表尾部。
查找,删除时:先定位哈希数组下标,再遍历该位置链表。
特点:无堆积冲突,最大时间复杂度是O(n)

封装哈希链表结点
包含存放的数据,指向下一个结点结构体的指针
typedef struct node
{
DataType_t data;
struct node*pnext;
}HSNode_t;
封装数据结构体
举例,通讯录 姓名 电话
typedef struct info
{
char name[32];
char tel[16];
}DataType_t;
宏定义哈希表大小
哈希表(哈希数组)大小有限 向内存申请有限的堆空间
#define HASH_MAX_SIZE 27
哈希函数
传入参数为选取的关键字 此处为姓名拼音的首字母,
利用(26个小写英文字母和26个大写英文字母)字符对应的ASIIC码表,返回0~25的数字,
若输入的不是汉字而是其他字符,返回哈希表空间数-1(26)。
int hash_function(char key)
{
if(key >='a' && key <= 'z')
{
return key - 'a';
}
else if(key >= 'A' && key <= 'Z')
{
return key - 'A';
}
else
{
return HASH_MAX_SIZE-1;
}
}
插入函数
传入参数为哈希数组的指针(二级指针)(要修改数组中的头指针必须传二级指针,即哈希数组的的指针),及存入哈希表的数据
调用malloc函数申请哈希结点的堆空间 ,进行是否申请成功判断
初始化结点:赋值数据,指针指空
调用哈希函数,用data.name[0] (即姓名拼音的首字母)计算哈希地址,定位到对应的桶
头插法,不用遍历链表找链尾,时间复杂度O(1)
新结点指向原链表头,更新链表头为新结点
int insert_hash_table(HSNode_t**hash_table,DataType_t data)
{
HSNode_t*pnode = malloc(sizeof(HSNode_t));
if(NULL == pnode)
{
printf("malloc error\n");
return -1;
}
pnode->data = data;
pnode->pnext = NULL;
int addr = hash_function(data.name[0]);
pnode->pnext = hash_table[addr];
hash_table[addr] = pnode; //phead 数组名
return 0;
}
遍历哈希表
传入参数为哈希数组的指针(二级指针)
外层for循环,以哈希数组的容量为条件,更改哈希数组的指针,遍历哈希表里的每一个桶
内层while循环,以指针不为空为条件,遍历输出并打印桶里的每一个数据,注意添加换行符
void show_hash_table(HSNode_t**hash_table)
{
for(int i = 0;i<HASH_MAX_SIZE;i++)
{
HSNode_t*ptmp = hash_table[i];
while(ptmp != NULL)
{
printf("%s : %s\n",ptmp->data.name,ptmp->data.tel);
ptmp = ptmp->pnext;
printf("\n");
}
}
}
哈希表查找函数
返回哈希结点的函数
传入参数为哈希数组的指针(二级指针),及指向主函数中表示查找到的数据的变量的指针(变量的地址)
调用哈希函数,用姓名拼音首字母计算哈希地址,定位到对应的桶
定义局部变量表示这个桶的链表头结点
while循环,遍历桶里的整条链表,调用strcmp函数,比较姓名,一致时直接返回结点
HSNode_t*find_hash_table(HSNode_t**hash_table,char*name)
{
int addr = hash_function(name[0]);
HSNode_t*ptmp = hash_table[addr];
while(ptmp != NULL)
{
if(0 == strcmp(ptmp->data.name,name))
{
return ptmp;
}
ptmp = ptmp->pnext;
}
return NULL;
}
销毁哈希表函数
传入参数为哈希数组的指针(二级指针)
外层for循环,以哈希数组容量为条件,遍历每一个桶,定义局部变量表示桶的链表头结点
内层while循环,循环删除桶里的所有结点,采用头删法,
原头结点的下一个结点成为头结点,释放原头结点的内存,变量指针移动到下一个结点,继续删除
void destory_hash_table(HSNode_t**hash_table)
{
for(int i = 0;i < HASH_MAX_SIZE;i++)
{
HSNode_t*ptmp = hash_table[i];
while(hash_table[i]!= NULL)
{
hash_table[i] = ptmp->pnext;
free(ptmp);
ptmp = hash_table[i];
}
}
}
更多推荐



所有评论(0)