【数据结构--散列函数】

散列查找的基本思想

在记录的存储位置和它的关键码之间建立一个确定的对应关系H,使得每个关键码key和唯一的存储位置H(key)相对应。

存储记录时,根据这个对应关系找到关键码的映射地址,并按此地址存储该记录;查找记录时,根据这个对应关系找到待查找关键码的映射地址,并按此地址访问该记录。

采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表(hash table),将关键码映射为散列表中适当存储位置的函数称为散列函数(hash function),所得到的存储位置成为散列地址(hash address)。

散列函数的设计原则

  1. 计算简单。散列函数不应该有很大的计算量,否则会降低查找效率。
  2. 函数值(即散列地址)分布均匀,希望散列函数能够把记录以相同的概率“散列”到散列表的所有地址空间中,这样才能保证存储空间的有效利用,并减少冲突。

三种常见的散列函数

1. 直接定址法

直接定址法的散列函数是关键码的线性函数,即:
H(key) = a × key + b(a,b为常数)
例如,关键码集合为{10,30,50,70,80,90},选取H(key) = key/10。则散列表如下图所示。
在这里插入图片描述
直接定址法的特点是不会产生冲突,但实际应用中能使用这种散列函数的情况很少。它适用于事先知道关键码的分布,关键码集合不是很大且连续性较好的情况。

2. 除留余数法

除留余数法的基本思想是:选择某个适当的正整数p,以关键码除以p的余数作为散列地址,即:
H(key) = key mod p
可见这个方法的关键在于选取合适的p,否则容易产生同义词。例如,若p含有质因子,例如p = m × n,则所有含有m或n因子的关键码的散列地址均为m或n的倍数,如下图所示
在这里插入图片描述
显然,这增加了冲突的机会。一般情况下,若散列表表长为m,通常选p小于或等于表长(最好接近m)的最小素数或不包含小于20质因子的合数。

3. 平方取中法

平方取中法是对关键码平方后,按散列表大小,取中间若干位作为散列地址(简称平方后截取),其原理是一个数平方后,中间的几位分布较均匀,从而冲突发生的概率较小。
例如,对于关键码1234,假设散列地址是2位,由于1234×1234=1522756,选取中间的两位作为散列地址,可以选22也可以选27.
平方取中法通常用在事先不知道关键码的分布且关键码的位数不是很大的情况。

处理冲突的方法

通常情况下,由于关键码的复杂性和随机性,很难找到理想的散列函数。如果某记录按散列函数计算出的散列地址加入散列表时产生了冲突,就必须再找一个地方来存放它,因此,需要有合适的处理冲突方法。下面列出两种常用的处理冲突的方法。

1. 开放定址法

开放定址法处理冲突的方法是,如果由关键码得到的散列地址产生了冲突,根据(H(key) + d)% m 寻找下一个空的散列地址。
例:设关键码集合{47,7,29,11,16,92,22,8,3},散列表表长为11,散列函数为H(key) = key mod 11,用开放定址法处理冲突,得到的散列表如图所示,具体过程如下:
H(47)=3,H(7)=7,没有冲突,直接存入;
H(29)=7,发生冲突,(H(29)+1)%11=8,散列地址8为空,将29存入;
H(11)=0,H(16)=5,H(92)=4,没有冲突,直接存入;
H(22)=0,发生冲突,(H(22)+1)%11=1,散列地址1为空,将22存入;
H(8)=8,发生冲突,(H(8)+1)%11=9,散列地址9为空,将8存入;
H(3)=3,发生冲突,(H(3)+1)%11=4,仍然冲突,(H(3)+2)%11=5,仍然冲突,(H(3)+3)%11=6,散列地址6为空,将3存入

在这里插入图片描述
算法定义:

int HashTableSearch(int *ht, int k){
	int i, j = H(k);	//计算散列地址
	i = j;				//设置比较的起始位置
	while(ht[i] != 0){
		if(ht[i] == k) return i;	//查找成功
		else i = (i + 1) % MaxSize;	//向后探测一个位置
	}
	return -1;	//查找失败
}
2. 拉链法(链地址法)

基本思想: 将所有散列地址相同的记录,及所有关键码为同义词的记录存储在一个单链表中——称为同义词子表(synonym table),在散列表中存储的是所有同义词子表的头指针。
开散列表的类定义和查找方法

template <typename DataType>
class Node{
public:
    Node<DataType> *next;
    DataType data;
};

const int MaxSize = 100;
class HashTable{
public:
    HashTable();    //构造函数,初始化开散列表
    ~HashTable();   //析构函数,释放同义词子表结点
    int Insert(int k);
    int Delete(int k);
    Node<int> *Search(int k);
private:
    int H(int k);
    Node<int> *ht[MaxSize];
};

HashTable::HashTable(){
    for (int i = 0; i < MaxSize; ++i)
        ht[i] = nullptr;
}

HashTable::~HashTable(){
    Node<int> *p = nullptr, *q = nullptr;
    for (int i = 0; i < MaxSize; ++i){
        p = q = ht[i];
        while(p != nullptr){
            p = p->next;
            delete q;
            q = p;
        }
    }
}

Node<int>* HashTable::Search(int k){
    int j = H(k);   //计算散列地址
    Node<int> *p = ht[j];   //工作指针p初始化
    while(p != nullptr){
        if(p->data == k) return p;
        else p = p->next;
    }
    return nullptr; //查找失败
}
  • 16
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值