散列

散列

散列表

在顺序表,二叉搜索树中,关键字值的存储位置与关键字值本身之间不存在着确定关系,
搜索某键值的元素,需要进行一系列关键字值与键值的比较,
搜索效率取决于搜索过程中所做的比较次数.

散列表的定义

假定关键字值与它的存储位置之间存在着某种对应关系H,
使得每个关键字与它的存储位置相对应,即Loc(key)=H(key),
Loc(key)表示关键字值为key的存储位置.
这个把关键字值映射到存储位置的函数H称为散列函数(也称为哈希函数 Hash function),
而如此建立的关键字值存储表称为散列表(也称为哈希表 Hash table).	 

散列表的ADT

ADT HT
数据:
有限个关键字值所组成的集合.
运算:
	Create()      创建散列表
	Display()     输出散列表
	Search()      搜索某关键值
	ASL()         计算成功搜索的平均长度

如果散列表的长度为m,一般用顺序表HT[0....m-1]来表示散列表.
散列表是另一种表示关键字值集合的数据结构.
在理想情况下,在散列表中搜索某个键值,不必进行关键字值的比较,因此搜索速度更快.
一般来说,关键字值集合要比散列表的地址集合大得多,有可能经过散列函数的计算,
把不同的关键字值映射到同一个散列地址,这就产生了冲动.
H(key1)=H(key2), (key1!=key2)
构建散列表,解决或减少冲突问题.

散列函数

散列函数满足:计算简单,具有均匀性.
常见的散列函数有:
m为散列表长度,p一般为不超过m的素数. % 取余运算 
(1) 除留余数法
	H(key)=key % p   
(2) 数字分析法
	假设关键字值集合中的每个关键字值都是有相同个数的字符或者数字组成,
	分析所有的关键字值,并从中提取分布均匀的若干位或组合作为地址.
(3) 折叠法
	折叠法是将关键字值转换成一串数字后,先将这串数字拆分成多个部分,最后在
	把它们加起来,即可算出这个关键字值的地址.
(4) 拉链法
	为每个散列地址建立一个单链表,单链表中存储所有具备该散列地址的同义词.
	散列链表不存在所谓的冲突问题.

溢出处理的闭散列表

解决冲突的方法又称为溢出处理技术,因为任何一种散列函数都难以避免冲突的产生,
因此选择好的溢出处理方法十分重要.一种常用的方法是闭散列法,
最常见的闭散列法有:线性探测法,二次探测法,双散列法.
(1) 线性探测法
线性探测法是当冲突发生时,以线性的方式向后寻找空的存储位置,一旦找到位置就把
关键字值存入进去.线性探测法将散列表的位置视为一个环状结构,
若后面的位置已被填满而前面还有位置时,可以将关键字值存入前面位置.
d(i)=H(key)=key % p;d(i+1)=(d(i)+1) % m.
线性探测法的缺点:同义词经常会聚集在一起. 
(2) 二次探测法
d(i)=H(key)=key % p;d(i+1)=(d(i) (+|-) i^2 ) % m.
(3) 双散列法				 
双散列法是一开始设置一系列的散列函数,
如果使用第1种散列函数出现冲突,就是用第二种散列函数; 
如果使用第2种散列函数出现冲突,就是用第三种散列函数, 
一直到没有冲突为止. 

散列表的存储和搜索(C++实现)

//采取二次探测指针法解决冲突问题
#include<iostream>
#include<iomanip>
using namespace std;
//创建哈希表 
void Create(int *HT,int *key,int m,int n,int p){
	int i,j,d,dl,c,sign;
	//初始化 
	for(j=0;j<m;j++){
		HT[j]=NULL;   //哈希表置空 
		HT[m+j]=0;	  //搜索次数置为0 
	}
	//关键字值集合存入哈希表
	for(i=0;i<n;i++){
		d=key[i]%p;      //除留余数法计算key[i]的地址号 
		c=1;			 //比较次数置初值 
		if(HT[d]==NULL){ //如果地址d处未存入关键字 
			HT[d]=key[i];//key[i]存入地址d处 
			HT[m+d]=c;	 //比较次数存入对应位置 
		}else{           //地址d已存入关键字,即冲突产生 
			sign=1;		 //符号变量先取1
			j=1;         //探测变量先取1
			do{
				dl=(d+sign*j*j)%m; //平方探测下一个地址 
				c++;			   //比较次数增1 
				if(sign==1)         
					sign=-1;       //符号变量反号,j不变,做下一次平方探测 
				else{
					sign=1;		   //符号变量反号
					j++;		   //探测变量增1,再做下一轮平方探测  
				}	
			}while(HT[dl]!=NULL);  //直到探测到空位置为止 
			HT[dl]=key[i];         //key[i]存入地址dl处 
			HT[m+dl]=c;            //比较次数存入对应位置 
		}
	} 
} 
//部分哈希表的输出格式 
void Output(int *HT,int start,int end,int m){
	int j;
	cout<<setw(8)<<"地    址";
	for(j=start;j<end;j++)
		cout<<setw(5)<<j;
	cout<<endl;
	
	cout<<setw(8)<<"关键字值";
	for(j=start;j<end;j++)
		cout<<setw(5)<<HT[j];
	cout<<endl;			
	
	cout<<setw(8)<<"搜索次数";		
	for(j=start;j<end;j++)
		cout<<setw(5)<<HT[m+j];
	cout<<endl;						 
}	

void Display(int *HT,int m){
	int i,j;
	j=m/10;
	for(i=0;i<=j;i++){
		if(i<j)
			Output(HT,10*i,10*(i+1),m);
		else if(i==j)
			Output(HT,10*i,m,m);	
	}
} 


void ASL(int *HT,int m,int n){
	int c=0;
	for(int j=0;j<m;j++)
		c=c+HT[m+j];
	cout<<"成功搜索所需平均比较次数为"<<c<<"/"<<n<<endl;	
} 
int main(){
	int key[12]={19,1,23,14,55,20,84,27,68,11,10,77};
	int HT[2][19];
	int m=19,n=12,p=13;
	Create(&HT[0][0],&key[0],m,n,p);
	cout<<"哈希表"<<endl;
	Display(&HT[0][0],m);
	ASL(&HT[0][0],m,n);
}

哈希链表存储和搜索(C++实现)

#include<iostream>
#include<iomanip>
#include<stdlib.h>
using namespace std;

//结点定义
struct Node{
	int data;    //数据域 
	Node *next;	 //指针域 
}; 
//创建哈希链表
void CreateHT(Node *HT,int *key,int m,int n,int p){
	int i,d;
	Node *NewNode,*q;
	//创建哈希链链表表头 
	for(i=0;i<m;i++){
		HT[i].data=i;
		HT[i].next=NULL;
	} 
	for(i=0;i<n;i++){
		d=key[i]%p;
		NewNode=new Node;
		NewNode->data=key[i];
		NewNode->next=NULL;
		q=&HT[d];    //探测指针q指向第d号链表
		if(q->next==NULL)
			q->next=NewNode;
		else{
			while(q->next!=NULL)
				q=q->next;
			q->next=NewNode;	
		}	  
	}
} 
//输出哈希表
void DisplayHT(Node *HT,int m){
	Node *q;
	cout<<"哈希链表"<<endl;
	for(int i=0;i<m;i++){
		q=&HT[i];
		while(q!=NULL){
			cout<<setw(2)<<q->data<<"=>";
			q=q->next;
		}
		cout<<"NULL"<<endl;	
	} 
}

//搜索哈希链表
void Search(Node *HT,int m,int p,int key){
	Node *q;
	int d,k,flag;
	d=key%p;
	k=0;
	flag=0;
	q=HT[d].next;
	while(q!=NULL){
		if(q->data==key){
			k++;
			flag=1;
		}
		q=q->next;
	}
	if(flag==1)
		cout<<"找到"<<k<<"个"<<key<<endl;
	else
		cout<<"找不到"<<key<<endl;	 
}
//创建关键字值集合 
void Createkey(int *key,int n){
	int delta=rand() % 9+1;  //设置增量初值
	for(int i=0;i<n;i++){
		key[i]=delta;
		delta=delta+rand()%7+1;
	} 
} 
//输出关键字值
void Displaykey(int *key,int n){
	cout<<"关键字值集合"<<endl;
	int ColNum=0;
	for(int i=0;i<n;i++){
		cout<<setw(4)<<key[i];
		ColNum++;
		if(ColNum==12){
			cout<<endl;
			ColNum=0;
		}		
	}
	cout<<endl; 
} 

int main(){
	int key[50];
	Node HT[7];
	int m=7,n=50,p=7;
	Createkey(key,n);
	Displaykey(key,n);
	CreateHT(HT,key,m,n,p);
	DisplayHT(HT,m);
	int Key;
	while(1){
		cout<<"输入搜索键值,输入-1退出";
		cin>>Key;
		if(Key==-1) break;
		Search(HT,m,p,Key); 
	}	
}

写于2020-10-29

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值