哈希表(散列表)

哈希(Hash)又叫散列表,是一种直接记录存放地址的方法,它的关键码与存储位置之间通过哈希函数建立了映象。

一、构造哈希函数

1.直接定址法

哈希函数取关键字的线性函数:

H(key)=a*key+b

        这种方法不会产生冲突,但需要提前知道关键字的集合,并且仅适用于地址集合的大小与关键字集合的大小相等的情况。

在实际应用中能使用这种哈希函数的情况很少。

2.数字分析法

在尽量减少冲突的前提下,取关键字的某几位作为地址。

数字分析法仅适用于事先明确知道表中所有关键码每一位数值的分布情况,并且如果换一个关键码集合,则选择哪几位需要重新决定。

3.平方取中法

以关键字的平方值的中间几位作为地址,目的是扩大差别,此方法在词典处理中使用广泛。

4.折叠法

分为移位叠加和间界叠加:

5.除留余数法

最最最常用的

取关键字小于等于哈希表长的数p,关键字÷p所余的数为哈希地址

H(key)=key\ MOD\ p

p为素数

 二、处理冲突的方法

遇到冲突要找其余能放的地方放下该关键字。

1.开放定址法

取哈希函数为H(key)=key%11

① 线性探测再散列

    即遇到冲突后往后+1,只要有空位置,总能找到一个不发生冲突的地址

输入:哈希表长m,关键字个数n;随后再输入n个关键字

输出:构造的哈希表结果,为空则输出null

#include <iostream>
using namespace std;
int a[100];
int main(){
	int m,n;cin>>m>>n;  //表长m,关键字个数n 
	for(int i=0;i<=m;i++) a[i]=1205;
	for(int i=1;i<=n;i++){
		int k;cin>>k;
		int key=k%11;
		while(1){
		    if(a[key%m]==1205) {  //取余的原因是要循环,可以当成一个循环队列来看 
				a[key%m]=k;
				break;
			}
			key++;
		}		
	}
	for(int i=0;i<m;i++)
	  if(a[i]==1205) cout<<"null ";
	  else cout<<a[i]<<" ";
	return 0;
}

② 二次探测再散列

     即遇到冲突后d= 1, -1, 4, -4, 9, -9……

输入:哈希表长m,关键字个数n;随后再输入n个关键字

输出:构造的哈希表结果,为空则输出null

#include <iostream>
using namespace std;
int a[100],b[6]={1,-1,4,-4,9,-9};
int main(){
	int m,n;cin>>m>>n;  //表长m,关键字个数n 
	for(int i=0;i<=m;i++) a[i]=1205;
	for(int i=1;i<=n;i++){
		int k;cin>>k;
		int key=k%11;
		int ke=key;
		for(int i=0;;i++){
		    if(a[ke%m]==1205) {  //取余的原因是要循环,可以当成一个循环队列来看 
				a[ke%m]=k;
				break;
			}
			ke=key;   //从基准的位置开始移动
			ke+=b[i];
			if(ke<0) ke+=m;
		}		
	}
	for(int i=0;i<m;i++)
	  if(a[i]==1205) cout<<"null ";
	  else cout<<a[i]<<" ";
	return 0;
}

2.再哈希法

构造若干个哈希函数,当发生冲突时,计算下一个哈希地址,直到冲突不再发生,即:

H_{i}=Rh_{i}(key)

Rh:不同的哈希函数

特点:不易产生聚集,但会增加计算时间。

3.链地址法

就是邻接表,也叫拉链法。

输入:

第一行:n,表示有n个数据;再输入n个数据。

第二行:q,表示有q个需要查询的数据;如果这个数据不在该表中,则加入该数据。

输出:

对应数据,以及对应数据的查找次数;如果查找失败则输出error

(1)头插法 用链式前向星实现:

#include <iostream>
#define MAX 100
using namespace std;
struct EDGE {
	int to, next;
} edge[MAX];
int cnt = 0, head[MAX];
void add_edge(int u, int v) {
	cnt++;
	edge[cnt].to = v,edge[cnt].next = head[u],head[u] = cnt;
}
int main() {
	int n, k, q;
	cin >> n;
	while (n--) {
		cin >> k;
		int key = k % 11;
		add_edge(key, k);
	}
	cin >> q;
	while (q--) {
		cin >> k;
		int key = k % 11, flag = 0, cnt = 0;
		for (int i = head[key]; i; i = edge[i].next) {
			int v = edge[i].to;
			cnt++;   //记录查找次数 
			if (v == k) {
				flag=1;
				break;
			}
		}
		if (!flag) {
			add_edge(key, k);
			cout << "error" << endl;
		} else
			cout << key << " " << cnt << endl;
	}
	return 0;
}

(2) 尾插法 

用二维的数组vector实现

#include <iostream>
#include <vector>
#define MAXN 100
using namespace std;
vector<int> v[MAXN]; 
int main() {
	int n, k, q;
	cin >> n;
	while (n--) {
		cin >> k;
		int key = k % 11;
		v[key].push_back(k);
	}
	cin >> q;
	while (q--) {
		cin >> k;
		int key = k % 11,flag=0,cnt=0;
		for(int i=0;i<v[key].size();i++){
			cnt++;
			if(k==v[key][i]){
				flag=1;
				break;
			}
		}
		if (!flag) {
			v[key].push_back(k);
			cout << "error" << endl;
		} else
			cout << key << " " << cnt << endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值