zoukankan      html  css  js  c++  java
  • 哈希表的构造方法、冲突处理方法及哈希拉链法的简单代码实现

      由于哈希表的查找高效性,在平时的算法中用的也是比较多。例如:字符串、单词个数的统计,只出现一次字符或者数字的统计,两个集合相同元素的查找等等,还有插入删除的高效(链地址法)都可以用哈希表来解决。所以这里对其做一个小小的总结。缺点可能是需要占用额外的内存空间。

    一、哈希函数的构造方法
    下面介绍五种常用的哈希构造方法:
    构造哈希函数的原则是:
    (1)函数本身便于计算;
    (2)计算出来的地址分布均匀,即对任一关键字k,f(k) 对应不同地址的概率相等,目的是尽可能减少冲突。
    1、除留余数法;
     取关键字被某个不大于哈希表长m的数p除后所得的余数为哈希地址。即:
              H(key)=key MODE p,p<=m.(p的取值最好为素数)。
     若冲突较多,可取较大的m和p值。
    2、随机法;
    采用一个伪随机函数做哈希函数,即:
             H(key)=random(key)。其中random为随机函数。
    通常,当关键字长度不等时采用此法构造哈希函数较为恰当。
    3、平方取中法;
    当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。
    这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
    例如对于关键key:123。1234^2=1522756,H(k)关键字的哈希地址为:227.
    4、折叠法;
          这种方法是按哈希表地址位数将关键字分成位数相等的几部分(最后一部分可以较短),然后将这几部分相加,舍弃最高进位后的结果就是该关键字的哈希地址。具体方法有折叠法与移位法。移位法是将分割后的每部分低位对齐相加,折叠法是从一端向另一端沿分割界来回折叠(奇数段为正序,偶数段为倒序),然后将各段相加。
    例如:key=12360324711202065,哈希表长度为1000,则应把关键字分成3位一段,在此舍去最低的两位65,分别进行移位叠加和折叠叠加,求得哈希地址为105和907。

    5、直接定址法;
    取关键字或关键字的某个线性函数值为哈希地址。即:
            H(key)=key  或 H(key)=a*key+b
    其中a、b为常数(这种hash函数叫做自身函数)。
    6、数字分析法;
      如果事先知道关键字集合,并且每个关键字的位数比哈希表的地址码位数多时,可以从关键字中选出分布较均匀的若干位,构成哈希地址。
    例如,有1000个记录,关键字为10位十进制整数d1d2d3…d7d8d9d10,如哈希表长取1200,则哈希表的地址空间为:000~1199。假设经过分析,各关键字中 d3、d5和d7的取值分布较均匀,则哈希函数为:h(key)=h(d1d2d3…d7d8d9d10)=d3d5d7。
    例如,h(3748597089)=457,h(9846372561)=432。就是找数字中分布均匀的数字。
    二、处理冲突的方法:
    1、开放定址法,又称下标加1法

    这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。这种方法有一个通用的再散列函数形式:
        Hi=(H(key)+di)% m   i=1,2,…,n
        其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不同,相应的再散列方式也不同。主要有以下三种:
      (1)线性探测再散列
      (2)二次探测再散列
      (3)伪随机探测再散列
        缺点是:线性探测再散列容易产生“二次聚集”。当删除某个数据的时候,需要设置标记或者移动数据,否则会导致查找的中断。
    2、再哈希法:
    这种方法是同时构造多个不同的哈希函数:
        Hi=RH1(key)  i=1,2,…,k
    当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
    3、链地址法;需要额外的空间;
       这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
    4、公共溢出区;
    这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表
    三、哈希表拉链法的具体实现
     链地址法(拉链法)
    当存储结构是链表时,多采用拉链法,用拉链法处理冲突的办法是:把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。T中各分量的初值应为空指针。
    哈希表拉链法查找的具体实现代码:

    #include <iostream>
    using namespace std;
    #define  MODLE 13
    struct Haxi_Table
    {
        int data;//记录一共有多少数据
        char a;
        Haxi_Table *next;
    };
    
    Haxi_Table *haxi_table[MODLE];//哈希表数组;
    void Create_Haxi(int arry[],int num)
    {
        for(int i=0;i<num;i++)
        {
            int index=arry[i]%MODLE;
            Haxi_Table *temp=new Haxi_Table;
            temp->a=i+97;
            temp->data=num;
            temp->next=NULL; 
            if(!haxi_table[index])
            {
                haxi_table[index]=temp;
            }
            else
            {
                temp->next=haxi_table[index];
                haxi_table[index]=temp;
            }
        }
    }
    char FindValue(int value)
    {
        int index=value%MODLE;
        Haxi_Table *p=haxi_table[index];
        while(p)
        {
            if(p->data=value)
            {
                return p->a;
            }
            else
            {
                p=p->next;
            }
        }
        return -1;
    }
    void DestoryHash()
    {
        Haxi_Table *temp=NULL;
        for(int i=0;i<MODLE;i++)
        {
            if(haxi_table[i])
            {
                while(haxi_table[i])
                {
                    temp=haxi_table[i];
                    haxi_table[i]=haxi_table[i]->next;
                    delete temp;
                }
            }
        }
    }
    int main()
    {
        int num;
        cout<<"please input the number of your data:"<<endl;
        cin>>num;
        int *array=new int[num];
        cout<<"please input the "<<num<<" data:"<<endl;
        for(int i=0;i<num;i++)
            cin>>array[i];
        Create_Haxi(array,num);
        cout<<"查找结果,8对应的字符为:"<<FindValue(8)<<endl;
        DestoryHash();
        system("pause");
        return 0;
    }
  • 相关阅读:
    cubestore driver 添加auth认证
    cubestore 添加auth 认证
    基于s3 扩展cubestore
    cube.js 预聚合检查
    cube.js 集成cubestore 时间格式问题的一些说明
    cube.js 集成cubestore 时间格式问题
    Building a GraphQL to SQL Compiler on Postgres, MS SQL and MySQL
    支持minio cubestore docker 镜像
    数据治理怎么做
    使用project制定项目计划可以分为六个步骤
  • 原文地址:https://www.cnblogs.com/qixinbo/p/7965466.html
Copyright © 2011-2022 走看看