查找算法-散列表

  • 当存储记录时,通过散列函数计算出记录的散列地址
  • 当查找记录时,我们通过同样的是散列函数计算记录的散列地址,并按此散列地址访问该记录


例一:有一个从1到100岁的人口数字统计表,其中,年龄作为关键字,哈希函数取关键字自身。
即:f(key) = key



例二:如果现在要统计的是1980年以后出生的人口数,那么我们对出生年份这个关键字可以变换为:用年份减去1980的值来作为地址。
即:f(key) = key – 1980




数字分析法通常适合处理关键字位数比较大的情况,例如我们现在要存储某家公司员工登记表,如果用手机号作为关键字,那么我们发现抽取后面的四位数字作为散列地址是不错的选择。


平方取中法是将关键字平方之后取中间若干位数字作为散列地址。



折叠法是将关键字从左到右分割成位数相等的几部分,然后将这几部分叠加求和,并按散列表表长取后几位作为散列地址
  • 此方法为最常用的构造散列函数方法,对于散列表长为m的散列函数计算公式为:

f(key) = key mod p(p<=m)

  • 事实上,这个方法不仅可以对关键字直接取模,也可以通过折叠、平方取中后再取模。
    例如下表,我们对有12个记录的关键字构造散列表时,就可以用f(key) = key mod 12的方法。


  • p的选择是关键,如果对于这个表格的关键字,p还选择12的话,那得到的情况未免也太糟糕了:



    p的选择很重要,如果我们把p改为11,那结果就另当别论啦:



  • 选择一个随机数,取关键字的随机函数值为它的散列地址。

  • 即:f(key) = random(key)。

  • 这里的random是随机函数,当关键字的长度不等时,采用这个方法构造散列函数是比较合适的。





  • 所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。

  • 它的公式是:
    fi(key) = (f(key)+di) MOD m (di=1,2,…,m-1)

  • 例:假设关键字集合为{12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34},使用除留余数法(m=12)求散列表


  • 可以修改di的取值方式,例如使用平方运算来尽量解决堆积问题:

    • fi(key) = (f(key)+di) MOD m (di=1²,-1²,2²,-2²…,q²,-q²,q<=m/1)
  • 还有一种方法是,在冲突时,对于位移量di采用随机函数计算得到,我们称之为随机探测法:

    • fi(key) = (f(key)+di) MOD m (di是由一个随机函数获得的数列)



例:假设关键字集合为{12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34},同样使用除留余数法求散列表。




例:假设关键字集合为{12, 67, 56, 16, 25, 37, 22, 29, 15, 47, 48, 34},同样使用除留余数法求散列表。
#define HASHSIZE 12
#define NULLKEY -32768

typedef struct
{
    int *elem;  // 数据元素的基址,动态分配数组
    int count;  // 当前数据元素的个数
}HashTable;

int InitHashTable(HashTable *H)
{
    H->count = HASHSIZE;
    H->elem = (int *)malloc(HASHSIZE * sizeof(int));
    if( !H->elem )
    {
        return -1;
    }
    for( i=0; i < HASHSIZE; i++ )
    {
        H->elem[i] = NULLKEY;
    }
    return 0;
}

// 使用除留余数法
int Hash(int key)
{
    return key % HASHSIZE;
}

// 插入关键字到散列表
void InsertHash(HashTable *H, int key)
{
    int addr;
    
    addr = Hash(key);
    
    while( H->elem[addr] != NULLKEY )   // 如果不为空,则冲突出现
    {
        addr = (addr + 1) % HASHSIZE;   // 开放定址法的线性探测
    }
    
    H->elem[addr] = key;
}

// 散列表查找关键字
int SearchHash(HashTable H, int key, int *addr)
{
    *addr = Hash(key);
    
    while( H.elem[*addr] != key )
    {
        *addr = (*addr + 1) % HASHSIZE;
        if( H.elem[*addr] == NULLKEY || *addr == Hash(key) )
        {
            return -1;
        }
    }
    
    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 基本概念 基于线性表、树表结构的查找方法,这类查找方法都是以关键字的比较为基础的。在查找过程中只考虑各元素关键字之...
    官先生Y阅读 515评论 0 2
  • 概念 散列技术: 在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f...
    liangxifeng833阅读 2,595评论 1 3
  • 1. 散列表的基本概念 元素的存储位置和其关键字之间建立某种直接关系,这就是散列查找法。 (1) 散列函数和散列地...
    yinxmm阅读 1,890评论 0 0
  • 查找 查找表是由同一类型的数据元素(或记录)构成的集合。关键字是数据元素中某个数据项的值,也称为键值,用它可以标识...
    keeeeeenon阅读 2,003评论 0 3
  • 时间,就这么淡淡的过去......你,仍记得十多年前的自己吗? 我们会在哪里,你们又会在何方?多年的约定,你是...
    新的开始_08fd阅读 426评论 0 0