哈希表及其常用算法(代码实例)

<hash表的特性>

Hash 表是使用 O(1) 时间进行数据的插入删除和查找,但是 hash 表不保证表中数据的有序性,这样在 hash 表中查找最大数据或者最小数据的时间是 O(N) 。

<寻址和 hash 函数>

理想状态下 hash 足够大,每一数据保存在一个 hash 存储单元内,这样对于插入删除和查找某一个数据就可以直接得到。但是现实情况下 hash 表不可能无限大,而且理论上保存的数据的个数是没有限制的,这样保存的数据的数量就远远大于 hash 表的存储单元的数量。

为了实现在 O(1) 内对数据进行插入删除和查找,就必须将一个数据映射到 hash 表中的固定位置,这个映射函数就是 hash 函数。 Hash 函数通过对数据进行计算得到一个在 hash 表中的位置地址。

 图 1.1 理想的 hash 表

要选择较好的 hash 函数,以及 hash 表存储单元的数量,这样才能使保存在 hash 表中的数据均匀分布。理想状态不太可能实现,由于存储的数据数量远远大于 hash 表存储单元的数量,所以再好的 hash 函数也可能使不同的数据得到相同的映射位置,这就造成了冲突。但是好的 hash 函数可以将这种冲突降到最低。

<分离链接>

解决这种冲突的第一种方法是借助链表来实现,就是将数据实际存放在与 hash 表存储单元相链接的链表中,而不是 hash 的存储单元中。

图 2.1 分离链表

当产生冲突的时候,将两个数据都链接在同一 hash 存储单元保存的链表中。当一个存储单元保存的链表中有多个数据的时候,对于链表后面的数据的查找添加和删除就是不是严格意义上的 O(1) 了。一个好的 hash 函数可以使得这个链表很短。最坏情况下,当所有的数据都保存在一个 hash 单元指定的链表中的时候,那么这个 hash 就和链表一样了。

<开放地址>

使用开放地址方法解决冲突的时候,数据仍然保存在 hash 表的存储单元中,但是当冲突发生的时候,要再次计算新的地址。

常用的开放地址法是线性探查,就是当对一个数据进行插入删除或者查找的时候,通过 hash 函数计算,发现这个位置不是要找的数据,这时候就检查下一个存储单元,一直找到要操作的数据为止。

除了线性探查外还有二次探查,再 hash 等等方法,都是当一次计算得到的位置不是要找到的数据的时候,怎样再次确定新的位置。

<完全 hash 表>

采用分离链表的方式解决冲突的时候,当多个数据被映射到一个地址的时候,它们就形成了一个链表,要操作这其中的一个数据,那么就必须在这个链表中进行操作。如果 hash 函数选择的好的话,链表会很短,这样的操作近似 O(1) ,但并不是精确的等于 O(1) ,它与链表的长度有关。对于数据的访问的最坏情况的访问也是 O(1) 的 hash 叫做完全 hash 表。

这样的 hash 表是一个两级的 hash 表,第一级的 hash 与使用分离链接方法的 hash 一样,但是 hash 存储单元中指向的不是一个链表,而是另一个 hash 表。

图 4.1 完全 hash 表

要小心的选择一级以及二级的 hash 函数可以完全保证在二级 hash 表中不会出现冲突。

?常用算法

?直接定址法 :地址集合 和 关键字集合大小相同

?数字分析法 :根据需要hash的 关键字的特点选择合适hash算法,尽量寻找每个关键字的 不同点

?平方取中法:取关键字平方之后的中间极为作为哈希地址,一个数平方之后中间几位数字与数的每一位都相关,取得位数由表长决定。比如:表长为512,=2^9,可以取平方之后中间9位二进制数作为哈希地址。

?折叠法:关键字位数很多,而且关键字中每一位上的数字分布大致均匀的时候,可以采用折叠法得到哈希地址,除留取余法除P取余,可以选P为质数,或者不含有小于20的质因子的合数

?随机数法:通常关键字不等的时候采用此法构造哈希函数较恰当。

?实际工作中需要视不同的情况采用不同的hash函数

?考虑因素:计算哈希函数所需要的时间,硬件指令等因素。

?关键字长度

?哈希表大小

?关键字分布情况

?记录查找的频率。(huffeman树)

?具体代码如下

#include<stdlib.h>
#include<math.h>
struct HashTable;
struct ListNote;
typedef struct HashTable *HashTbl;
typedef struct ListNote *Position;
typedef Position List;
int Hash(int key,int tablesize);
int NextPrime(int x);
HashTbl InitalizeTable(int TableSize);
void DestroyTable(HashTbl H);
Position Find(int key,HashTbl H);
void Insert(int key, HashTbl H);
void Delete(int key,HashTbl H);
struct HashTable{
    int TableSize;
    Position *TheList;
};
struct ListNote{
    int element;
    Position next;
};
int Hash(int key,int tablesize){
    return key%tablesize;
}
int NextPrime(int x){
    int flag;
    while(1){
        flag = 0;
        int i;
        int n = sqrt((float)x);
        for(i = 2 ;i <= n;i++){
            if(x % i == 0){
                flag = 1;
                break;
            }
        }
        if(flag == 0)
            return x;
        else
            x++;
    }
}
HashTbl InitalizeTable(int TableSize){
    if(TableSize <= 0){
        printf("散列大小有问题\n");
        return NULL;
    }
    HashTbl table = (HashTbl)malloc(sizeof(struct HashTable));
    if(table == NULL)
        printf("分配失败");
    table->TableSize = NextPrime(TableSize);
    table->TheList = (Position*)malloc(sizeof(List) * table->TableSize);
    if(table->TheList == NULL)
        printf("分配失败");
    table->TheList[0] = (Position)malloc(table->TableSize*sizeof(struct ListNote));
    if(table->TheList == NULL)
        printf("分配失败");
    int i;
    for(i = 0;i < table->TableSize;i++){
        table->TheList[i] = table->TheList[0] + i;
        table->TheList[i]->next = NULL;
    }
    return table;
}
Position Find(int key,HashTbl H){
    Position p;
    List L = H->TheList[Hash(key,H->TableSize)];
    p = L->next;
    while(p != NULL && p->element != key)
        p = p->next;
    if(p == NULL)
        return L;
    else
        return p;
}
void Insert(int key,HashTbl H){
    Position p,NewCell;
    p = Find(key,H);
    if(p->element != key){
        NewCell = (Position)malloc(sizeof(struct ListNote));
        if(NewCell == NULL)
            printf("分配失败");
        else{
            p = H->TheList[Hash(key,H->TableSize)];
            NewCell->next = p->next;
            p->next = NewCell;
            NewCell->element = key;
        }
    }
    else
        printf("已经存在该值了\n");
}
void Delete(int key,HashTbl H){
    Position p ,NewCell;
    p = Find(key,H);
    if(p->element == key){
        NewCell = H->TheList[Hash(key,H->TableSize)];
        while(NewCell->next != p)
            NewCell = NewCell->next;
        NewCell->next = p->next;
        free(p);
    }
    else
        printf("没有该值");
}
int main(){
    HashTbl table = InitalizeTable(10);
    Position p = NULL;
    p = Find(10,table);
    printf("%d\n",p->element);
    Insert(55,table);
    Insert(90,table);
    Insert(35,table);
    Insert(33,table);
    p = Find(55,table);
    printf("%d\n",p->element);
    p = Find(33,table);
    printf("%d\n",p->element);
    Delete(33,table);
    Delete(44,table);
    system( "pause" );
    return 0 ;
}  
 

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

原文地址:https://www.cnblogs.com/big-devil/p/8590242.html

时间: 2024-11-09 05:01:35

哈希表及其常用算法(代码实例)的相关文章

哈希表之bkdrhash算法解析及扩展

BKDRHASH是一种字符哈希算法,像BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等,这些都是比较经典的,通过http://blog.csdn.net/wanglx_/article/details/40300363(字符串哈希函数)这篇文章,我们可知道,BKDRHash是比较好的一个获取哈希值的方法.下面就讲解这个BKDRHash函数是如何推导实现的. 当我看到BKDRHash的代码时,不禁就疑惑了,这里面有个常

HDU1880——哈希表(BKDR算法)——魔咒词典

哈希表:建表复杂度:O(n) k为字符串品均长度,n为字符串数目 查询 O(1) !!! 实现主要操作 1.把字符串进行实数化 2.把这个串存进去如果发现该位置为空,直接存,如果已经有了值,拉出一个链表 3.查询 该算法好像是不会产生冲突,因为一旦产生冲突,那么strcmp字符串函数就会对他们进行比较,虽然复杂度会高一些 转载出处——http://blog.csdn.net/hlyfalsy/article/details/9238537 蒟蒻只是做了一点解释 /****************

【数据结构】线性表&amp;&amp;顺序表详解和代码实例

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 预备知识 1.0 什么是线性表? 线性表(List)是零个或者多个数据元素的有限序列. 首先它是一个序列.里面的元素是有顺序的,如果有多个元素,除开头和结尾以外的元素都有一个前驱和一个后继.而开头元素只有后继,结尾元素只有前驱. 其次线性表是有限的,也就是里面的元素个数是有限的. 1.1 线性表的基本操作(描述) 1ADT 线性表(List) 2Data 3    线性表的数据对象集合为{a1, a2, a3, ...

HTML 表单常用的代码元素

表单: 将数据通过浏览器提交到服务器的媒介.<form action="" method="get/post" ></form> get 提交有长度限制 post 提交无长度限制 一般常用post  表单元素:12个 1.文本类1).<input type="text" value=""> - 文本框 在文本框中输入内容.... 2).password - 密码框 3).<textar

通达OA 常用组件代码实例(逐渐补充完善)

1.选择日期控件 <input type="text" name="chengWenRiQi" cols="20" size=20 maxlength="20" onClick="WdatePicker()"> 默认显示当前日期 <input type="text" name="chengWenRiQi" cols="16" si

数据结构之哈希表

最近看PHP数组底层结构,用到了哈希表,所以还是老老实实回去看结构,在这里去总结一下. 1.哈希表的定义 这里先说一下哈希表的定义:哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典,大家估计小学的时候也用过不少新华字典吧,如果我想要获取“按”字详细信息,我肯定会去根据拼音an去查找 拼音索引(当然也可以是偏旁索引),我们首先去查an在字典的位置,查了一下得到“安”,结果如下.这

php扩展开发-哈希表

什么是哈希表呢?哈希表在数据结构中也叫散列表.是根据键名经过hash函数计算后,映射到表中的一个位置,来直接访问记录,加快了访问速度.在理想情况下,哈希表的操作时间复杂度为O(1).数据项可以在一个与哈希表长度无关的时间内,计算出一个值hash(key),在固定时间内定位到一个桶(bucket,表示哈希表的一个位置),主要时间消耗在于哈希函数计算和桶的定位. 在分析PHP中HashTable实现原理之前,先介绍一下相关的基本概念: 如下图例子,希望通过人名检索一个数据,键名通过哈希函数,得到指向

自己动手实现java数据结构(五)哈希表

1.哈希表介绍 前面我们已经介绍了许多类型的数据结构.在想要查询容器内特定元素时,有序向量使得我们能使用二分查找法进行精确的查询((O(logN)对数复杂度,很高效). 可人类总是不知满足,依然在寻求一种更高效的特定元素查询的数据结构,哈希表/散列表(hash table)就应运而生啦.哈希表在特定元素的插入,删除和查询时都能够达到O(1)常数的时间复杂度,十分高效. 1.1 哈希算法 哈希算法的定义:把任意长度的输入通过哈希算法转换映射为固定长度的输出,所得到的输出被称为哈希值(hashCod

简单的JS表单验证效果代码

简单的JS表单验证代码:表单验证几乎是不可缺少的,有的表单验证是在后台完成的,有的则是使用JavaScript在在前端完成基本的验证,这样可以有效的减轻服务器的压力,下面就介绍一下JS实现的最简单的表单验证.代码实例如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://w