HashTable的拉链法的数组实现

来源:互联网 发布:台湾ptt大陆网络用语 编辑:程序博客网 时间:2024/04/28 08:41

哈希表又称散列表,是一种通过键值映射到值的数据结构,具有查找快速的优点。

哈希表的基本思想是:

1.以结点的关键字k为自变量,通过一个确定的哈希函数H,计算出对应的函数值H(k),作为结点的存储位置并将结点存入。
2.顺序查找、折半查找、树的查找是建立在比较基础上的查找,而哈希表的查找是直接查找
但是对于一个确定的哈希函数H,其对于所需存储数据计算所得的H(K)可能是相同的,这就是哈希表的冲突,即若某个哈希函数H对于不同的关键字得到相同的哈希地址,称为冲突


冲突举例:
比如对于已有关键词序列(14233992511),构造hash函数H为H(K)=K mod 7,通过hash函数建立含6个元素的hash表,如下:

14

23

9

  

39

25

11

  
  
其中23和9,39和25以及11冲突,应对这种冲突,哈希表有多个处理方法,比如开放定址法,拉链法,再哈希法……,这里介绍的是拉链法。


我们先来看看常规的拉链法,常规的拉链法可以理解为数组与多个单链表的结合,有点类似图中的邻接表。它是将所有哈希函数值相同的结点链接在同一个单链表中。若哈希函数的值域为0m-1,将散列表定义为一个由m个头指针组成的指针数组HT[m],凡是散列地址为i的结点,均插入到以HT[i]为头指针的单链表中。
例如给出关键词序列19,14,23,01,68,20,84,27,55,11,10),hash函数为H(K)=K%13,其构造的拉链法hash表如下图:
其中哈希表的数据只存在其指针数组所引出的单链表中。

我要讲的哈希表的数组实现也是基于拉链法,但是它是不创建单链表的,即所有的数据都存在一个数组中,它的思想类似图的存储结构中的链式前向星,不懂链式前向星的可以自行百度,但是对本文没有阅读障碍。
首先我们观察一下上图用常规拉链法所造出的哈希表,它有13个由头结点组成的数组,以及和关键词序列数目相同的节点,那么我们不妨创建一个由13个加与关键词序列数目的数组,其中hash节点定义如下:
struct hashNode{    int key;//键值    int value;//值    int postion;//指示位置};
其中postion用来存放数组下标,然后所定义的数组可以如下:
hashNode hashTable[24];//大小应定为所插入元素个数的2倍
其中前13个类比哈希表的头结点数组,后11个存放数据,然后将其全部节点的postion值都定义为-1,表示其中还没有存入数据:
memset(hashTable,-1,sizeof (hashTable));//偷了个懒
然后我们构造哈希函数:
int hashf(int key)//hash函数{    return key%11;}
并且我们定义一个辅助变量cnt,将其初始化为第一个存放数据的数组下标,现在是13;
int cnt=13;
当我们插入数据时我们先计算出它的hash值,比如第一个键值19,它的hash值是19%11=8,那么我们查找哈希表的第8个节点,发现它的postion值为-1,表明这个hash值还未插入数据,然后我们令这个节点的postion值等于此时的cnt值,再在cnt这个节点中装入hash节点,再让cnt++,这就是插入第一个未查找过的hash值的方法。那么如果这个hash值不是第一次被访问,比如我再插入一个关键词为8的哈希节点,那么我们可以通过一个while循环,找到第一个表中节点postion值不为-1的点,再重复插入操作,其代码如下,其中hashlen值是构造hash函数用的:
void hash_insert(int key,int value,int hashlen,int &cnt){    int pos=hashf(key,hashlen);    while (hashTable[pos].postion!=-1)//当pos位上的hash表已经存储了位置时,找到没有存储位置的hash表    {        pos=hashTable[pos].postion;    }    hashTable[pos].postion=cnt;    hashTable[cnt].key=key;//cnt为用于存储hash节点最近的空节点的下标    hashTable[cnt].value=value;    cnt++;}
那么我们现在构建好了哈希表,下一步就是根据键值查找hash元素,我们可以先通过算出hash函数,然后对其节点开始遍历,当该节点还有下一个指向的节点时,不断的进行循环,除非该节点就是我们所需寻找的节点,其中有一个注意点,那就是头结点的那个节点不应拿来比较键值是否相等,代码如下:
int hash_search(int key,int hashlen){    int pos=hashf(key,hashlen);    while (hashTable[pos].postion!=-1)//pos>=hashlen是因为hash表的前len个节点只存放位置,不存放数据    {        if (key==hashTable[pos].key&&pos>=hashlen)            return hashTable[pos].value;        else            pos=hashTable[pos].postion;    }    if (key==hashTable[pos].key&&pos>=hashlen)        return hashTable[pos].value;    printf("无法找到\n");    return 0x3f3f3f3f;}
至于哈希节点的删除,这件事就交给机智的你来完成了……
总的代码:
//类似于hash表的拉链法实现#include <iostream>#include <cstdio>#include <cstring>using namespace std;struct hashNode{    int key;//键值    int value;//值    int postion;//指示位置};hashNode hashTable[1000];//大小为hashlen的2倍即可int hashf(int key,int hashlen)//hash函数{    return key%hashlen;}void hash_insert(int key,int value,int hashlen,int &cnt){    int pos=hashf(key,hashlen);    while (hashTable[pos].postion!=-1)//当pos位上的hash表已经存储了位置时,找到没有存储位置的hash表    {        pos=hashTable[pos].postion;    }    hashTable[pos].postion=cnt;    hashTable[cnt].key=key;//cnt为用于存储hash节点最近的空节点的下标    hashTable[cnt].value=value;    cnt++;}int hash_search(int key,int hashlen){    int pos=hashf(key,hashlen);    while (hashTable[pos].postion!=-1)//pos>=hashlen是因为hash表的前len个节点只存放位置,不存放数据    {        if (key==hashTable[pos].key&&pos>=hashlen)            return hashTable[pos].value;        else            pos=hashTable[pos].postion;    }    if (key==hashTable[pos].key&&pos>=hashlen)        return hashTable[pos].value;    printf("无法找到\n");    return 0x3f3f3f3f;}int main(){    memset(hashTable,-1,sizeof (hashTable));    int hashlen=10,cnt=hashlen;//hashlen大小为所需插入元素个数    int key[10]= {2,3,1,4,13,21,31,10,11,19};    int value[10]= {4,2,6,10,37,72,17,3,92,11};    for (int i=0;i<=9;i++)    {        hash_insert(key[i],value[i],hashlen,cnt);//cnt与链式前向星的思路有点类似    }    for (int i=9;i>=0;i--)    {        int temp=hash_search(key[i],hashlen);        printf("%d ",temp);    }    return 0;}









 

 

 

0 0
原创粉丝点击