考察Hash算法。
两种方式:1.链地址法。 2.开放定址法。
1.链地址法。
优点:
1.各链表上的结点是动态申请的,故更适合于造表前无法确定表长的情况。
缺点:1.动态申请结点,new操作非常耗时。
2.指针域比较浪费空间。
解题思路:
利用Hash法,链地址法解决冲突。
key=( len1+len2+len3+len4+len5+len6)%prime
=( len1%prime +len2%prime +len3%prime +len4%prime +len5%prime +len6)%prime
为了避免出现大数,这里使用了同余模定理求key
注意,这里的key千万不能用平方和,本来这题时间就很紧凑了,乘法运算更加严重浪费时间,所以采用了连加求key,只要prime足够大,同样能够把地址冲突降低到最低,我取了10n(就是100W)内的最大的素数作为prime, prime=999983
#include<iostream>
using namespace std;
//***********常量定义*****************//
const __int64 MAX=10000001;
const __int64 prime=999983;
//**********题目变量*****************//
struct Node
{
__int64 len[6];//存储雪花六边的长度
};
Node leaf[MAX];//存储输入的雪花数据
class HashTable
{
public:
Node snow;
HashTable* next;
HashTable()
{
next=NULL;
}
};
HashTable* Hash[prime+1];
//计算第K个雪花的Hash值
__int64 CmputeKey(int k)
{
__int64 key=0;
for(int i=0;i<6;i++)
{
key+=(leaf[k].len[i])%prime;
key%=prime;
}
++key;
return key;//键值后移1位,把key的范围从0~999982变为 1~999983
}
//比较两片雪花顺时针方向是否相同
bool clockwise(HashTable*p,int k)
{
bool flag=true;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(leaf[k].len[i]!=p->snow.len[(i+j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
//比较两片雪花逆时针方向是否相同
bool countclockwise(HashTable*p,int k)
{
bool flag=true;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(leaf[k].len[i]!=p->snow.len[(11-i-j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
//处理第k个结点
bool process(int k)
{
bool flag=false;
__int64 key;
key=CmputeKey(k);
//若Hash[Key]为NULL;
if(!Hash[key])
{
HashTable* temp=new HashTable;
for(int j=0;j<6;j++)
temp->snow.len[j]=leaf[k].len[j];
Hash[key]=temp;
}
else
{
HashTable* current=Hash[key];
HashTable* tail=NULL;
while(current)
{
if(clockwise(current,k)||countclockwise(current,k))
{
printf("%s","Twin snowflakes found.");
flag=true;
return flag;
}
else
{
tail=current;
current=current->next;
}
}
HashTable* temp=new HashTable;
for(int j=0;j<6;j++)
{
temp->snow.len[j]=leaf[k].len[j];
}
tail->next=temp;
}
return flag;
}
int main()
{
int N;//输入的数据
bool flag;
scanf("%d",&N);
memset(Hash,0,sizeof(Hash));
//采集数据
for(int i=1;i<=N;i++)
{
for(int j=0;j<6;j++)
scanf("%d",&leaf[i].len[j]);
}
for(int i=1;i<=N;i++)
{
flag=process(i);
if(flag)
return 0;
}
printf("%s","No two snowflakes are alike.");
}
比较两片雪花是否相等:
设定雪花A,B。
A不动,B旋转6次,A跟B分别比较2*6次;分别是正向相等,和反向相等
正向相等:
A0==B0,A1==B1,......A5==B5;
反向相等:
A0==B5,A1==B4...........A5==B0
//比较两片雪花顺时针方向是否相同
bool clockwise(HashTable*p,int k)
{
bool flag=true;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(leaf[k].len[i]!=p->snow.len[(i+j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
//比较两片雪花逆时针方向是否相同
bool countclockwise(HashTable*p,int k)
{
bool flag=true;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(leaf[k].len[i]!=p->snow.len[(11-i-j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
2.第二种方法
对于碰撞,采用开放地址发,省时间,也省空间,因为没有了new操作,也没有指针开销
第二种方法:在VS2010上运行没有问题,对10w个数据也可以正确运行,但是poj现实runtime error.请大家赐教
#include<iostream>
#include<algorithm>
#include<fstream>
using namespace std;
//*******************常量定义***************
const int MAX=100003;
//*******************算法变量****************
struct Node
{
__int64 len[6];//存储六叶子
bool IsUsed;//是否已占用
};
Node Hash[MAX];//Hash表
__int64 snow[6];//存储输入的雪花
__int64 temp[6];
bool clockwise(__int64 leaf[6],__int64 k)
{
bool flag;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(Hash[k].len[i]!=leaf[(i+j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
bool countclockwise(__int64 leaf[6],__int64 k)
{
bool flag;
for(int j=0;j<6;j++)
{
flag=true;
for(int i=0;i<6;i++)
{
if(Hash[k].len[i]!=leaf[(11-i-j)%6])
{
flag=false;
break;
}
}
if(flag)
return true;
}
return false;
}
bool SetUpHash(__int64 Array[6])
{
int flag=false;
for(int j=0;j<6;j++)
temp[j]=Array[j];
sort(Array,Array+6);
__int64 Key=((Array[0]+Array[2]+Array[4])|(Array[1]+Array[3]+Array[5]))%MAX;
if(!Hash[Key].IsUsed)//若该Key值对应的结点还未使用
{
for(int j=0;j<6;j++)
Hash[Key].len[j]=temp[j];
Hash[Key].IsUsed=true;
}
else
{
int jump=1;
while(Hash[Key].IsUsed)//当该结点被占用
{
if(clockwise(temp,Key)||countclockwise(temp,Key))
{
printf("%s","Twin snowflakes found.\n");
return true;
}
else
{
Key=(Key+jump*jump)%MAX;
jump++;
}
}
//找到空闲位置,插入元素
for(int j=0;j<6;j++)
Hash[Key].len[j]=temp[j];
Hash[Key].IsUsed=true;
}
return false;
}
int main()
{
for(int i=0;i<MAX;i++)
Hash[i].IsUsed=false;
int N;//输入的雪花数
int flag=false;
/*scanf("%d",&N);*/
fstream read("ceshi.txt");
read>>N;
for(int i=1;i<=N;i++)
{
//输入雪花
for(int j=0;j<6;j++)
{
/*scanf("%d",&snow[j]);*/
read>>snow[j];
}
flag=SetUpHash(snow);
if(flag)
return 0;
}
printf("%s","No two snowflakes are alike.\n");
}