关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 算法设计 »基于BKDRhash实现Hash算法

    基于BKDRhash实现Hash算法

    2014-09-12 00:00:00 出处:Sign_
    分享

    哈希(Hash)算法,即散列函数。它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。hash算法一般用于快速查找和加密。

    hash算法可以使用的哈希函数种类很多,处理冲突的方法也有开放定址、再哈希、链地址、公共溢出区等。

    因此,在编写代码之前,首先需要根据所要处理的数据,选择合适的hash函数和冲突处理办法。开放定址需要空闲存储单元,所需要的表比实际容量大,而且容易产生二次聚集发生新冲突。链地址使用链表存储关键字,可以随时插入新数据,数据量大小不受限制。缺点是要用到指针,给新单元分配地址需要时间,会一定程度上减慢算法速度,但影响不大可以忽略。

    笔者需要处理的是一个10W行字符串的字典,关键字重复率高。因此选择适用于字符串的哈希函数,常用字符串哈希函数有 BKDRhash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等,个人倾向于BKDRHash,记忆和使用都很简便。

    BKDRHash函数代码如下:

    unsigned int BKDRhash(TYPE key)
      {//BKDRhash函数
          unsigned int seed = 131;
          unsigned int hash = 0;
    
          while(*key != 'n' && *key != 0)      //通常使用时,判别条件为*key != 0即可,此处的*key != 'n'是因笔者程序需要
              hash = hash * seed + (*key++);
    
          return hash % DICLEN;
     }

    对于关键字重复的冲突处理方法,笔者这里使用链地址法。hash表结构体如下:

    #define STRLEN 15
      #define DICLEN 100000
    
      typedef char* TYPE;
      typedef int BOOL;
    
      typedef struct _NODE{
          TYPE data;
          struct _NODE* next;
     }NODE;
    
     typedef struct _HASH_TABLE{
         NODE* phead;           //此变量可以不用,这里使用是为了减少其他函数中的重新定义过程
         NODE** chainhash;
     }HASH_TABLE;

    准备工作OK,整理好思路,可以开始编写hash算法了。O(∩_∩)O

    首先,创建一个hash表,并对哈希表,链表,头节点进行初始化。

    NODE* create_node()
      {//开辟节点
          NODE* pnode = (NODE*)malloc(sizeof(NODE));
          memset(pnode, 0, sizeof(NODE));
    
          pnode->data = (char*)malloc(STRLEN * sizeof(char));
          memset(pnode->data, 0, STRLEN * sizeof(char));
          pnode->next = NULL;
    
         return pnode;
     }
    
     HASH_TABLE* create_hash()
     {//创建hash表
         HASH_TABLE* new_hash_table = (HASH_TABLE*)malloc(sizeof(HASH_TABLE));
         memset(new_hash_table, 0, sizeof(HASH_TABLE));
    
         new_hash_table->phead = create_node();
         new_hash_table->chainhash = (NODE**)malloc(DICLEN * sizeof(NODE*));
    
         for(int i = 0; i < DICLEN; i++){        
             new_hash_table->chainhash[i] = (NODE*)malloc(sizeof(NODE));
             memset(new_hash_table->chainhash[i], 0, sizeof(NODE));
         }
    
         return new_hash_table;
     }

    插入数据

    链表的chainhash每个分量的初始状态都是空指针,凡是哈希函数值 BKDRhash(data)相同的记录,都插入同一个链表chainhash[i],此时i = BKDRhash(data)。该链表头结点不为空的话,指针就后移,在表尾插入新记录(表头、表尾插入均可,只要保持每次操作相同,即同一链表中的关键字有序)。

    BOOL insert_data(HASH_TABLE* hash, NODE* phead, TYPE data)
      {//插入新数据
          if(hash == NULL)
              return 0;
    
          if(hash->chainhash[BKDRhash(data)]->data == NULL){
              NODE* newnode = create_node();
    
              strcpy(newnode->data, data);
             newnode->next = NULL;
             hash->chainhash[BKDRhash(data)]->data = newnode->data;
             hash->chainhash[BKDRhash(data)]->next = newnode->next;
    
             free(newnode);
             return 1;
         }
    
         else{        
             phead = hash->chainhash[BKDRhash(data)];
    
             while(phead->next != NULL)
                 phead = phead->next;
    
             phead->next = create_node();
    
             strcpy(phead->next->data, data);
             phead->next->next = NULL;
    
             return 1;
         }
     }

    查找数据

    查找数据时,首先通过哈希函数值找到对应的链表,然后比较字符串内容。

    NODE* find_data(HASH_TABLE* hash, NODE* phead, TYPE data)
      {//查找数据
          phead = hash->chainhash[BKDRhash(data)];
    
          if(hash == NULL)
              return NULL;
    
          while(phead != NULL){
    
             if(strncmp(phead->data, data, STRLEN) == 0)
                 return phead;
             else
                 phead = phead->next;
         }
    
         return NULL;
     }

    删除数据

    删除数据类似于单链表的删除操作

    BOOL del_data(HASH_TABLE* hash, NODE* phead, TYPE data)
      {//删除数据
    
          phead->next = create_node();
          phead->next = hash->chainhash[BKDRhash(data)];
    
          if(hash == NULL)
              return 0;
    
         while(phead->next != NULL){
    
             if(strncmp(phead->next->data, data, STRLEN) == 0){
    
                 if(phead->next->data == hash->chainhash[BKDRhash(data)]->data)
                     hash->chainhash[BKDRhash(data)] = phead->next->next;
                 else
                     phead->next = phead->next->next;
    
                 return 1;
             }
             else
                 phead->next = phead->next->next;
         }
    
         free(phead->next);
    
         return 0;
     }

    修改数据

    修改数据非常简单,即先删除后插入

     BOOL alter_data(HASH_TABLE* hash, NODE* phead, TYPE data, TYPE new_data)
      {//修改数据
          if(hash == NULL)
              return 0;
    
          if(data == new_data)
              return 1;
    
          if(del_data(hash, phead, data) == 1){
    
             if(insert_data(hash, phead, new_data) == 1)
                 return 1;
             else
                 return 0;
         }
    
         else
             return 0;
     }

    这样,一个简单的hash算法就写好了!笔者冗长的测试代码如下。。。。至于为什么测试要写这么长,笔者也不造o(╯□╰)o

     int main(int argc, char* argv[])
      {//测试
          int i = 0;
          char* testdata = "kyxntghcxolgqlwn";
          char data[STRLEN + 2] = {0};
    
          HASH_TABLE* dic = create_hash();
    
          FILE* fp = fopen("dic.txt", "r+");
         assert(fp != 0);
    
         while(i < DICLEN){
             fgets(data, STRLEN + 2, fp);
             insert_data(dic, dic->phead, data);    
             i++;
        }
    
         //查找测试
         if(find_data(dic, dic->phead, testdata) != NULL)    
             printf("find it: %sn", (find_data(dic, dic->phead, testdata))->data);    
         else
             printf("no this data!n");
    
         //删除再查找测试
         if(del_data(dic, dic->phead, testdata) == 1)
             printf("delete it!n");
         else
             printf("try again!n");
    
         if(find_data(dic, dic->phead, testdata) != NULL)    
             printf("find it: %sn", (find_data(dic, dic->phead, testdata))->data);
         else
             printf("no this data!n");
    
         //修改数据测试
         testdata = "fpwdwpk";
         char* newdata = "bibibibibiun";
    
         if(alter_data(dic, dic->phead, testdata, newdata) == 1){
    
             if(find_data(dic, dic->phead, newdata) != NULL)    
                 printf("find it: %sn", (find_data(dic, dic->phead, newdata))->data);
             else
                 printf("no this data!n");
         }
    
         fclose(fp);
         free(dic);
    
         return 0;
     }
    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。