第一节——初赛中的Hash
哈希表,也称散列表,是一种高效的数据结构。它的最大优点就是把数据存储和查找所消耗的时间大大降低,几乎可以看成是O(1)的,而代价是消耗比较多的内存。在当前竞赛可利用内存空间越来越多、程序运行时间控制的越来越近的情况下,“空间换时间”的做法还是很值得的。
哈希冲突,不能保证每个元素的关键字与函数值是一一对应的,这样就产生了“冲突”。
解决冲突方法:
1、直接取余数法 h(k)=k mod m //m一般为一个大素数
关键字k除以m,取余数作为在Hash表中的位置。
2、乘积取整法;
3、平方取中法;
4、线性探测法
5、链地址法(后两种会在后面详细讲到,第二种和第三种可以自行了解,不强求)
查找算法
基本的查找算法有以下4种:
1、线性查找(挨个看呗)
2、二分查找(log级别每次折半查找,前提是所有元素必须是顺序排列的)
3、分块查找(块与块有序、块内无序,可以理解成块外用二分、块内用线性)
4、哈希查找(他来了,他来了!!)
哈希查找简介
1、哈希是Hash的音译,意译就是散列。哈希查找是一种按关键字地址的快速检索方法。
2、哈希与其他查找方法的不同之处
(1)哈希查找是对记录的关键字值进行某种运算,直接求出记录的地址
(2)快——关键字到地址直接转换,无序反复比较
(3)核心不在于如何“比较”,而在于如何“计算”
(4)最能体现计算机科学精髓的查找方法
哈希查找的基本思想:记录关键字与其存储地址之间的映射关系H(key),把某个较大的集合P映射到另一个较小的集合Q中,核心就是设计哈希函数,并构建哈希表。
哈希查找过程:
1、由给定的关键字key,根据哈希函数计算出对应的哈希地址H(key)
2、根据H(key)直接找到该记录的存储位置
哈希冲突与解决(系不系很多童鞋们都肥肠期待!!??)
当两个不同的数据的哈希值相同时,就会发生冲突(collision)
处理哈希冲突的常用方法之一:链地址法
链地址法具体步骤:
(1)将哈希值相同的数据存在一个链表中
(2)查找哈希表时,当查找到这个链表时必须采用线性查找方法
哈希函数典型构造方法——求模取余法
(1)H(key)=key % p
(2)p<=m,哈希表表长为m
(3)余数(哈希地址)总是循环出现,呈周期性变化
哈希表的特点
| 一般的线性表 | 哈希表 |
|
在记录的存储地址与它的关键字之间不存在确定的对应关系 |
在记录的存储地址和它的关键字之间存在一个确定的对应关系 |
|
在表中查找记录时,需要进行一系列 的关键字比较 |
每个关键字在表中有唯一的一个存储地址与之对应,直接由关键字找到存储地址 |
|
建立在“比较“的基础上,查找效率依赖于查找过程中所进行的比较次数 |
根据关键字直接计算出记录存放的地址查找效率高 |
第二节——Hash查询代码实现
哈希典型题
哈希构造的链表中查询方式比较快。已知存在一个随机的数据表(表中数字均为正整数,没有重复),数据个数n≤50000;现在给定一些整数,查询在表中是否存在。
二分代码实现(代码短,时间长)
#include<iostream>
#include<algorithm>
using namespace std;
int a[10005],l,r,mid;
int main()
{
int key,n,m;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a,a+n+1);
while(m--)
{
cin>>key;
l=1;
r=n;
while(l<=r)
{
mid=(l+r)/2;
if(a[mid]==key)
{
cout<<"yes/n";
break;
}
if(a[mid]<key)
{
l=mid+1;
}
if(a[mid]>key)
{
r=mid-1;
}
}
if(a[mid]!=key)
{
cout<<"no/n";
}
}
return 0;
}
哈希代码实现(代码长、时间短)
#include<iostream>
using namespace std;
const int N=50000;
const int b=999979,H=999979;
int tot,adj[H],nxt[N],num[N];
int top,stk[N];
void init()
{
tot=0;
while(top)
{
adj[stk[top--]]=0;
}
}
void insert(int key)
{
int h=key%b;
for(int e=adj[h];e;e=nxt[e])
{
if(num[e]==key)
{
return ;
}
}
if(!adj[h])
{
stk[++top]=h;
}
nxt[++tot]=adj[h],adj[h]=tot;
num[tot]=key;
}
bool query(int key)
{
int h=key%b;
for(int e=adj[h];e;e=nxt[e])
{
if(num[e]==key)
{
return true
}
}
return false;
}
int main()
{
int a[10000];
init();
int n,m;
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>a[i];
insert(a[i]);
}
cin>>m;
int num;
for(int i=1;i<=m;i++)
{
cin>>num;
if(query(num))
{
cout<<"yes"<<endl;
}
else
{
cout<<"no"<<endll;
}
}
return 0;
}
第三节——哈希冲突解决
1、顺序寻址法
设H(key)=key%9
若当前位置已经有原先的数据了,则放到下一个位置去,如果下一个位置仍然不为空,那么久继续往后走,直到找到第一个为空的元素位置把该元素放进去。
代码实现
int hash_table[N];
void push1(int x)
{
int y=x%N;
for(;hash_table[y]&&hash_table[y]!=x;)
{
y=(y+1)%N;
}
if(hash_table[y])
{
cout<<x<<"_has_occured_before!"<<endl;
}
else
{
hash_table[y]=x;
cout<<x<<"_inserted."<<endl;
}
}
2、链地址法(刚才说过了,不再多说了,直接上代码)
代码实现(用到了动态数组vector,在STL里面,不知道的可以去百度上搜搜教程)
推荐题目(洛谷):P1102、P3370、P2957、P1955、P13381、P5018
原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/273696.html