目录
转自这里:https://blog.youkuaiyun.com/txl199106/article/details/40184965
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。具体的介绍网上有很详细的描述,如闲聊哈希表 ,这里就不再累述了;
哈希表在像Java、C#等语言中是与生俱来的。可是在C的世界中,似乎只有自己动手,丰衣足食;在网上google了一把,大致有几个版本,我会一一来分析对比;
首先先来交代一下哈希表实现中需要注意的一些概念:
1、哈希函数
也叫散列函数,即:根据key,计算出key对应记录的储存位置
position = f(key)
散列函数满足以下的条件:
1、对输入值运算,得到一个固定长度的摘要(Hash value);
2、不同的输入值可能对应同样的输出值;
以下的函数都可以认为是一个散列函数:
f(x) = x mod 16; (1)
f(x) = (x2 + 10) * x; (2)
f(x) = (x | 0×0000FFFF) XOR (x >> 16); (3)
不过,仅仅满足上面这两条的函数,作为散列函数,还有不足的地方。我们还希望散列函数满足下面几点:
1、散列函数的输出值尽量接近均匀分布;
2、x的微小变化可以使f(x)发生非常大的变化,即所谓“雪崩效应”(Avalanche effect);
上面两点用数学语言表示,就是:
1, 输出值y的分布函数F(y)=y/m, m为散列函数的最大值。或记为y~U[0, m]
2,|df(x)/dx| >> 1;
从上面两点,大家看看,前面举例的三个散列函数,哪个更好呢?对了,是第三个:
f(x) = (x | 0×0000FFFF) XOR (x >> 16);
它很完美地满足“好的散列函数”的两个附加条件。
2、哈希冲突(Hash collision)
也就是两个不同输入产生了相同输出值的情况。首先,哈希冲突是无法避免的,因此,哈希算法的选择直接决定了哈希冲突发送的概率;同时必须要对哈希冲突进行处理,方法主要有以下几种:
1, 链地址法
链地址法:对Hash表中每个Hash值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中
2, 开放地址法
下面就来看看每种方法的具体实现吧:
链地址法:
举例说明: 设有 8 个元素 { a,b,c,d,e,f,g,h } ,采用某种哈希函数得到的地址分别为: {0 , 2 , 4 , 1 , 0 , 8 , 7 , 2} ,当哈希表长度为 10 时,采用链地址法解决冲突的哈希表代码如下所示。
————————————————
3、链地址法代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _node{
char *key;
char *value;
struct _node *next;
}node;
#define HASHSIZE 101
static node* hashtab[HASHSIZE];
void inithashtab(){
for(int i=0;i<HASHSIZE;i++){
hashtab[i]=NULL;
}
}
unsigned int hash(char *s){
unsigned int h=0;
while(*s!='\0'){
h=*s+h*31;
s++;
printf("%d\n",h);
}
return h%HASHSIZE;
}
node* lookup(char *n){
unsigned int id=hash(n);
node *node=hashtab[id];
while(node!=NULL){
if(strcmp(n,node->key)==0){
return node;
}
node=node->next;
}
return NULL;
}
char* m_strdup(char *s){
char *m=malloc(strlen(s)+1);
if(m==NULL)return NULL;
strcpy(m,s);
return m;
}
char* get(char* name){
if(lookup(name)!=NULL){
node*n=lookup(name);
return n->value;
}else return NULL;
}
int add(char* key,char* value){
node *n;
if((n=lookup(key))!=NULL){
free(n->value);
}else{
int id=hash(key);
n=malloc(sizeof(*n));
if(n==NULL)return 0;
n->key=m_strdup(key);
if(n->key==NULL)return 0;
n->next=hashtab[id];
hashtab[id]=n;
}
n->value=m_strdup(value);
if(n->value==NULL)return 0;
return 1;
}
void displaytable(){
node *np;
for(int i=0;i<HASHSIZE;i++){
if(hashtab[i]==NULL)continue;
else{
printf("(");
np=hashtab[i];
for(;np!=NULL;np=np->next)
printf("(%s , %s)",np->key,np->value);
printf(")\n");
}
}
}
void cleanup(){
node *cur,*pre;
for(int i=0;i<HASHSIZE;i++){
if(hashtab[i]==NULL)continue;
else{
cur=hashtab[i];
while(cur){
pre=cur->next;
free(cur->key);
free(cur->value);
free(cur);
cur=pre;
}
}
}
}
int main()
{int i;
char* names[]={"name","address","phone","k101","k110"};
char* descs[]={"Sourav","Sinagor","26300788","Value1","Value2"};
inithashtab();
//增加部分--------------------------打印hash_table_index
for(i = 0; i < 5; i++)
{
unsigned int hi=hash(names[i]);
printf("%s--hash_table_index=%d\n", names[i], hi);
}
//---------------------------------------打印hash_table_index
for(i=0;i<5;i++)
add(names[i],descs[i]);
displaytable();
printf("Done\n");
printf("If we didnt do anything wrong..""we should see %s\n",get("k110"));
add("phone","9433120451");
printf("Again if we go right, we have %s and %s\n",get("k101"),get("phone"));
displaytable();
cleanup();
return 0;
}
4、链地址法 hash表实战:1418. 点菜展示表
菜名一定是作为key,num数量作为值。查找菜名,找到了加1 。
table *tab[MAXTABLE]; 每个桌维护一个hash
foodhash* lookup(char* foodname, int tabid) {
unsigned int hi = hash(foodname);
foodhash* f = tab[tabid]->food[hi];
for (; f != NULL; f = f->next) {
if (strcmp(f->name, foodname) == 0)
return f;
}
return NULL;
}
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
#define MAXFOODNUM 1000
#define MAXTABLE 502
typedef struct _foodhash {
char* name;
int num;
struct _foodhash* next;
}foodhash;
typedef struct _table {
foodhash* food[MAXFOODNUM];
}table;
table *tab[MAXTABLE];
void initTable() {
for (int i = 0; i < 502; i++) {
tab[i] = NULL;
}
}
int s2i(char* s) {
int ret = 0;
while((*s)!=0){
ret = ret * 10;
ret = ret + *s - '0';
s++;
}
return ret;
}
char* i2s(int i) {
char* ret = malloc(10000);
int len = 0;
for (; i != 0; i = i / 10) {
ret[len++] = i % 10 + '0';
}
int j = len - 1;
for (int i = 0; i < j; i++) {
char temp = ret[i];
ret[i] = ret[j];
ret[j] = temp;
j--;
}
ret[len] = '\0';
return ret;
}
char* m_strdup(char* s) {
char* m = malloc(strlen(s) + 1);
if (m == NULL)return NULL;
strcpy(m, s);
return m;
}
unsigned int hash(char* s) {
unsigned int h = 0;
while (*s != '\0') {
h = *s + h * 31;
s++;
}
return h % MAXFOODNUM;
}
foodhash* lookup(char* foodname, int tabid) {
unsigned int hi = hash(foodname);
foodhash* f = tab[tabid]->food[hi];
for (; f != NULL; f = f->next) {
if (strcmp(f->name, foodname) == 0)
return f;
}
return NULL;
}
int add(char* foodname, int num, int tabid) {
unsigned int hi = hash(foodname);
foodhash* f = malloc(sizeof(*f));
if (f == NULL)return 0;
f->num = num;
f->name = m_strdup(foodname);
if (f->name == NULL)return 0;
f->next = tab[tabid]->food[hi];
tab[tabid]->food[hi] = f;
return 1;
}
void clear() {
for (int i = 0; i < 502; i++) {
if (tab[i] != NULL) {
for (int j = 0; j < MAXFOODNUM; j++) {
if (tab[i]->food[j] != NULL) {
foodhash* fd = tab[i]->food[j];
foodhash* t;
while (fd != NULL) {
t = fd->next;
free(fd->name);
free(fd);
fd = t;
}
}
}
free(tab[i]);
}
}
}
char *** displayTable(char *** orders, int ordersSize, int* ordersColSize, int* returnSize, int** returnColumnSizes){
initTable();
tab[501] = malloc(sizeof(table));
for (int k = 0; k < MAXFOODNUM; k++)
tab[501]->food[k] = NULL;
for (int i = 0; i < ordersSize; i++) {
int tabid = s2i(orders[i][1]);
if (lookup(orders[i][2], 501) == NULL)
add(orders[i][2], 1, 501);
if (tab[tabid] == NULL) {
tab[tabid] = malloc(sizeof(table));
for (int k = 0; k < MAXFOODNUM; k++)
tab[tabid]->food[k] = NULL;
add(orders[i][2], 1, tabid);
}
else {
foodhash* fd;
if ((fd = lookup(orders[i][2], tabid)) != NULL) {
fd->num++;
}
else {
add(orders[i][2], 1, tabid);
}
}
}
char*** ret = (char***)malloc(sizeof(char**) * 501);
ret[0] = (char**)malloc(sizeof(char*) * MAXFOODNUM);
for (int i = 0; i < MAXFOODNUM; i++) {
ret[0][i] = malloc(21);
}
ret[0][0] = "Table";
int line1 = 1;
for (int i = 0; i < MAXFOODNUM; i++) {
if (tab[501]->food[i] != NULL) {
foodhash* tmp = tab[501]->food[i];
while (tmp != NULL) {
ret[0][line1++] = m_strdup(tmp->name);
tmp = tmp->next;
}
}
}
for (int i = 1; i < line1-1; i++) {
for (int j = i+1; j < line1; j++) {
if (strcmp(ret[0][i], ret[0][j]) > 0)
{
char tmp[20];
strcpy(tmp, ret[0][i]);
ret[0][i] = malloc(20);
strcpy(ret[0][i], ret[0][j]);
ret[0][j] = malloc(20);
strcpy(ret[0][j], tmp);
}else continue;
}
}
*returnSize = 1;
*returnColumnSizes = malloc(sizeof(int) * (501));
for (int j = 0; j < MAXTABLE-1; j++) {
if (tab[j] != NULL) {
ret[*returnSize] = (char**)malloc(sizeof(char*) * line1);
ret[*returnSize][0] = malloc(10);
ret[*returnSize][0] = i2s(j);
for (int i = 1; i < line1; i++) {
ret[*returnSize][i] = malloc(10);
foodhash* fd;
if ((fd = lookup(ret[0][i], j)) != NULL) {
ret[*returnSize][i] = i2s(fd->num);
}
else ret[*returnSize][i] = "0";
}
(*returnSize)++;
}
}
for (int i = 0; i < (*returnSize); i++) {
(*returnColumnSizes)[i] = line1;
}
return ret;
}