【hash】 !一次学懂 ! 例题 ——》》SSL1125 集合【hash】 & SSL1692 魔板【hash】【BFS】

—— 哈希表 ( H a s h    T a b l e ) (Hash~~Table) Hash  Table作为一种高效的数据结构,它正在竞赛中发挥着越来越重要的作用。


简单解释:

我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数( h a s h hash hash函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;

构建hash函数:

常用的有以下几种方法:

  1. 除余法

取一个正整数m,用m去除关键码,取其余数作为地址,即: h ( K e y ) = K e y m o d    m h(Key)= Key\mod m h(Key)=Keymodm

  1. 数字分析法

关键码的位数比存储区域的地址的位数多,在这种情况下可以对关键码的各位进行分析,丢掉分布不均匀的位留下分布均匀的位作为地址。

  1. 平方取中法

将关键码的值平方,然后取中间的几位作为散列地址。

  1. 折叠法

折叠法是将关键码从某些地方断开,分关键码为几个部分,其中有一部分的长度等于地址码的长度,然后将其余部分到它的上面,如果最高位有进位,则把进位丢掉

例: 如关键码Key=58422241,要求转换为3位的地址码。
分析:分如下3段:5 8 4 | 2 2 2 | 4 1,则相加:
5 8 4
2 2 2
  4 1        
8 4 7
h(Key)=84

冲突处理:

线性重新散列技术易于实现且可以较好的达到目的。

令数组元素个数为 S S S ,当 h ( k ) h(k) h(k) 已经存储了元素的时候,
依次探查 ( h ( k ) + i ) m o d    S ( i ∈ N ∗ ) (h(k)+i) \mod S\quad (i\in N^*) (h(k)+i)modS(iN) ,直到找到空的存储单元为止
如果探查完整个哈希表都没有空位,那么就是 h a s h hash hash数组开了。

hash数组主要功能:

  1. 插入元素;
  2. 查找元素。

例题:

SSL1125 集合

在这里插入图片描述

思路:

注意到这个问题的本质就是对于给定的集合A ,确定集合B中的元素是否在集合A中。所以,我们使用 h a s h hash hash表来处理。至于哈希函数,只要按照除余法就行了, H ( x ) = x m o d    m H(x) = x \mod m H(x)=xmodm.

补充: m m m尽量是一个大质数,如:15889,149993,100003等。

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#define m 149993
using namespace std;
long long n,n2,x,ans;
long long a[m];
int h(int y)  //hash
{
    
    
	return y%m;
}
int hw(int z)    //集合A里每个元素在hash数组里定位
{
    
    
	int j=0;
    int hx=h(z);
    while(j<m&&a[(hx+j)%m]!=0&&a[(hx+j)%m]!=x)
      j++;
    return (hx+j)%m;
}
int main()
{
    
    
   scanf("%lld",&n);
   for(int i=1; i<=n; i++)
    {
    
    
     	scanf("%lld",&x);
     	a[hw(x)]=x;
    }
   scanf("%lld",&n2);
   for(int i=1; i<=n2; i++)
    {
    
    
    	scanf("%lld",&x);
    	if(a[hw(x)]==x)  //对比 A 和 B
    	  ans++;
    }
   if(ans==n&&ans==n2) cout<<"A equals B";
   else if(ans==n) cout<<"A is a proper subset of B";
   else if(ans==n2) cout<<"B is a proper subset of A";
   else if(ans==0) cout<<"A and B are disjoint";
   else cout<<"I'm confused!";
   return 0;
}

SSL1692 魔板

在这里插入图片描述

思路:

该题目很明显是用搜索做,如果用深度优先搜索,则可能出现死循环,而且时间很长,所以只能用宽度优先搜索。但是如果没有一种好的方法来判断是否已经加入列表,时间可能会超时,所以: h a s h hash hash。按照初始状态标号,则共有8!=40320个情况。根据公式:
在这里插入图片描述

可以将每一种情况唯一对应到0~40319中的一个数。再用宽度优先搜索可以很轻松的实现这一算法。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#define p 100003
using namespace std;
const int ci[3][8]={
    
    {
    
    8,7,6,5,4,3,2,1},{
    
    4,1,2,3,6,7,8,5},{
    
    1,7,2,4,5,3,6,8}};
int c,num[p],fa[p],tail,head;
string mb,a[p],s[p];
char dl[p];
bool hash(string s)
{
    
    
	int ans=0;
	for(int i=0; i<8; i++)
	   ans=(ans<<3)+(ans<<1)+s[i]-48;   //乱搞hash
	int j=0,hx=ans%p;
	while(j<p&&a[(hx+j)%p]!=""&&a[(hx+j)%p]!=s)   //将 hash 和 定位 结合
	  j++;
	if(a[(hx+j)%p]=="")
	 {
    
    
	 	a[(hx+j)%p]=s;
	 	return 0;
	 }
	else
	  return 1;
}
void bfs()
{
    
    
	hash("12345678");
	s[1]="12345678";
	head=0,tail=1;
	while(head<tail)
	 {
    
    
	 	head++;
	 	for(int i=0; i<3; i++)
	 	 {
    
    
	 	 	tail++;
	 	 	num[tail]=num[head]+1;
	 	 	fa[tail]=head;     //存储路径,方便输出
	 	 	s[tail]="";
	 	 	if(i==0)      //存储状态
	 	 	  dl[tail]='A';
	 	 	else if(i==1)
	 	 	  dl[tail]='B';
	 	 	else if(i==2)
	 	 	  dl[tail]='C';
	 	    for(int j=0; j<8; j++)
	 	     s[tail]+=s[head][ci[i][j]-1];
	 	    if(hash(s[tail]))
	 	      tail--;
	 	    else if(s[tail]==mb)
			  return;
	 	 }
	 }
}
void write(int x)
{
    
    
	if(x==1) return;
	write(fa[x]);   //回到每个状态输出
	printf("%c",dl[x]);
}
int main()
{
    
    
    for(int i=1; i<=8; i++)
     {
    
    
     	scanf("%d",&c);
     	mb+=c+48;
     }
    if(mb=="12345678")
      printf("0");
    else
     {
    
    
     	bfs();
     	printf("%d\n",num[tail]);
     	write(tail);
     }
    return 0;
}

当然,hash的学习远不止这些,剩下的得由自己慢慢探索… …

推荐几道题给大家练习:

  1. 洛谷P1102
  2. 洛谷P4305
  3. 洛谷T24242

最后,第一次码这么长的文章,希望大家能多多指教!

猜你喜欢

转载自blog.csdn.net/Jackma_mayichao/article/details/107447031