哈希表+哈希桶简介及实现

4 篇文章 0 订阅

最近在学习通信协议,链路层传输时,对于报文的识别处理,建立哈希识别规则;之前简单学习过哈希,还没深入过,闲来无事把学习的内容通俗的表达出来,也和大家分享一下,让新入门的童鞋很快理解。

1、哈希表

我们都学过函数,三要素:自变量x,因变量y,对应关系f。
在函数里x、y通过映射关系f,一一对应起来。

那什么是哈希呢?

就是记录的储存位置和他的关键字之间建立一个确定的对应关系f,这里我们就可以吧这种对应关系f称之为哈希(Hash)函数

那哈希表呢?
采用哈希函数将记录储存在一块连续的存储空间中,这块连续的储存空间就称之为哈希表(Hash table)

看到这你应该是明白定义了吧。

当然哈希表不仅可以用来储存,更是可以用来查找,效率是很高的,因为一一对应嘛,查找算法时间复杂度为O(1)!哈希表的建立的关键是哈希函数的构建,也就是如何形成keyvalue的对应。

理想情况当然是一一对应的关系最好,但是现实情况下,难免碰到f(key1)=f(key2),我们称之为冲突,key1同key2我们可以称为“同义词”所以构建哈希函数,处理冲突也就是很关键的事情了。

这里简单介绍几个建立哈希关系的方法,但不做深入探究,有兴趣的童鞋可以自行查找学习。

1.1直接定址法

f(key)=a*key+b
即取关键字的某个线性函数作为散列(哈希)地址;但我们需要知道关键字的分布,现实情况很少用。

1.2数字分析法

对于刚刚入学的童鞋都会有个学号,而前几位肯定相同,后面几位每个人都不同,这时(不用)分析便可知道,抽取后面几位,对这些数字进行操作,使之不相同,总而言之就是提供一个哈希函数,能够让关键字分配到哈希表中去。

1.3平方取中法

也就是说将数字平方,取中间几位作为关键字。

1.4除数取余法

假如你有n个数字
f(key)=key%p(p=<n)
也可平方后操作,当然这里关键在于找到合适的p

key01234567891011
value122538151629786756212247

这里p=12,正好一一对应,嘿嘿嘿。

1.5随机数法

f(key)=random(key)
就是用了random函数
额还有很多不写了。

然而事实情况是:构建这样的函数很难,所以要去解决这种冲突
也就是解决key1不等于key2,但f(key1)=f(key2)。

方法不少,列举一下吧,就不细说了,有兴趣的童鞋自行搜索学习。
1、开发定址法 2、再散列函数法 3、链地址法

2 哈希桶

实际上哈希桶是解决哈希表冲突的一种方法。常见的解决冲突的两种方法:1、分离链接法 2、开放定址法。其中使用分离链接法,得到的对应关系即为哈希桶。

哈希表中同一个位置可能存有多个元素,为应对哈希冲突问题,将哈希表中的每个位置表示一个哈希桶

2.1哈希桶示意

每个key被hash(…)到相同的孔,那么如何解决冲突呢 就是在那个孔放一个链表
这个孔下面的链表就像桶一样盛着key对应的元素
下面通过除数取余+分离链接画图简单说明下:
在这里插入图片描述
这里将冲突的元素插入到各桶头部,即链表头。

哈希桶就是盛放不同key链表的容器,在这里我们可以把每个key的位置看作是一个桶,桶里放了一个链表,故该方法也叫分离链接法。

另外,前面有说过,哈希表的查找算法时间复杂度是O(1),学过算法的同学,肯定知道哈希是以空间换取时间的,如果这里哈希通的key选择不当,复杂度将迅速升为O(n)(当全部元素对应一个key值,退化为链表)

2.1哈希桶简单实现

代码很不难,学习的小伙伴耐心分析看看;我定义的节点里包括key和value,实际上只需要一个值就可以生成对应哈希值;另外由于我定义时,用的数组,所以生成key是连续的,只是简单示例下。

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

#define BUCKETCOUNT 10

/*链表节点*/
struct Node{
	char* key;
	char* value;
	struct Node *next;
};

typedef struct Node node;

/*顺序哈希桶*/
struct hashTable{
    node bucket[BUCKETCOUNT];/*先默认定义10个桶*/
};

typedef struct hashTable table;


void initHashTable();
int hash(const char* key);
struct Node* lookup(const char* key);
int insertnode(table *t, char *key, char *value);
const char* get(const char* key);
void display();

/*初始化哈希桶*/
void initHashTable(table *t){
    int i;
    if(!t) 
        return;

    for(i = 0; i < BUCKETCOUNT; ++i){
        t->bucket[i].key = NULL;
        t->bucket[i].next = NULL;
        t->bucket[i].value = NULL;
    }
}

/*哈希函数,根据生成哈希值*/
int hash(const char* key){
	int hash=0;
	for (; *key; ++key)
	{
		hash=hash*33+*key;
	}
	return hash%BUCKETCOUNT;
}

/*插入数据到hash表中*/
int insertnode(table *t,char *key,char *value){
    int index, vlen1, vlen2;
    node *e, *ep;

    if(t == NULL || key == NULL || value == NULL) return -1;

    index = hash(key);
    //如果现在桶为空,直接写入第一个桶节点
    if(!t->bucket[index].key){
        t->bucket[index].key = strdup(key);
        t->bucket[index].value = strdup(value);
    }
    else{
        e = ep = &(t->bucket[index]);
        /*先从已经存在的找*/
        while(e){
            /*如果key值重复,替换相应的值*/
            if(strcmp(e->key, key) == 0){
                vlen1 = strlen(value);
                vlen2 = strlen(e->value);
                if(vlen1 > vlen2){
                    free(e->value);
                    e->value = (char *)malloc(sizeof(vlen1 + 1));
                }
                memcpy(e->value, value, vlen1 + 1);
                return index;/*插入完成*/
            }
            ep = e;
            e = e->next;
        }
        /*没有在当前桶中找到,创建新的节点加入,并加入桶链表*/
        e = (node *)malloc(sizeof(node));
        if(NULL != e){
            e->key = strdup(key);
            e->value = strdup(value);
            e->next = NULL;
            ep->next = e;
        }
    }
    return index;
}

/*打印hash表*/

void display(table *t){
    int i;
    node *e;
    if(!t) return;

    for(i = 0; i < BUCKETCOUNT; i++){
        printf("bucket[%d]:", i);
        e = &(t->bucket[i]);
        while(e){
            printf("%s = %s\t\t",e->key, e->value);
            e = e->next;
        }
        printf("\n");
    }
}

int main(int argc, char* argv[])
{
	table *ht;
	ht = (table *)malloc(sizeof(table));
    initHashTable(ht);
	char* key[]={"a","b","c","d","e","f","g","h","i","j",
                "k","l","m","n","o","p","q","r","s","t",
                "u","v","w","x","y","z"};
	char* value[]={"apple","banana","china","doge","egg","fox",
                "goat","hello","ice","jeep","key","love",
                "mirror","net","orange","panda","quarter","rabbit",
                "shark","tea","unix","volley","wolf","x-ray","yard","zoo"};
	for (int i = 0; i < 26; ++i)
	{
		insertnode(ht, key[i], value[i]);
	}
	display(ht);
	return 0;
}

执行下看看效果
在这里插入图片描述

PS:
函数**strdup()**
头文件: string.h
功 能: 将串拷贝到新建的位置处,strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
返回值: 返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值