您的位置:首页 > 理论基础 > 数据结构算法

[数据结构]第七章-集合与符号表

2016-12-21 16:37 155 查看
集合

· 用位向量实现ADT集合并进行集合的并、交等运算:

用一个n位的向量v来存储集合A的特征函数值(为1为属于集合,为0不属于集合)可以唯一地表示集合A。优点是判断元素是否属于集合、元素的插入和删除都只要用O(1)的时间。缺点是当n很大时占空间很大,有一个办法是一个数组位置存放一个大数而非只放一位0或1,大数的二进制串就是A中一段元素的特征函数值。

符号表

·符号表是以集合为基础,支持成员运算(判断是否属于集合)、插入元素、删除元素三种运算的抽象数据类型。可以用表示集合的链表或位向量或一个定长数组来存储,但缺点在于受数组大小限制,并且储存空间一般没有得到充分利用。

· 用散列表实现符号表

散列:建立键值与其存储位置的关系,是一种映射。xi –> H(xi)

1.开散列 将集合中元素划分为有限个类,每一个类称为一个桶,x属于第i个桶当且仅当H(x) = i 。



如图中的情况,等概论情况下的成功查找平均时间为,(2×1+1×2)/3(有两个元素1步可找到,有一个2步找到)

2.闭散列 每个桶都只存放集合中的一个元素。

使用线性探测法,加入元素的时候从H(x)开始找一个空桶放下去,若H(x)已被占,则继续往后找,H(x)+1,…,H(x)max,0,1,…H(x)-1 直到找到空桶。 查找元素时从H(x)开始往后找,如果一直找到空桶了都还没找到x则x不在集合中,但如果做过删除运算时这样可能出错(遇到了被删掉元素的空桶判断停下,其实x还在后面)。所以加入state数组给每个桶标记,为0时表示从未被占,1为曾被占,2为被占。

实现散列所需考虑的:

1.散列函数的选取

我们所希望的是将n个元素均匀散列到B个桶中去:

①除余法 选择一个正整数m,m不超过桶数B,H(k) = k%m

②数乘法 选择一个纯小数a(0 < a < 1), H(k) = (int)(B(ka-(int)ka))

③平方取中法 当桶数B不是10的方幂,而键是0~n之间的整数时,可选取与B互质的整数c使得Bc^2与n^2大致相等, H(k) = (int)(k^2 / c)%B

④基数转换法 将键值看成用另一个进制表示的数后,再将它转换为原来进制表示的数,取其中若干位作为散列函数值。一般取大于原来基数的数作为转换的基数,并且这两个基数互质。

⑤随机数法 H(k) = random(k) 通常当键的长度不等时采用此法构造效果较好

2.解决冲突

为避免散列表中成块的连续地址被占用的聚集现象应该采用跳跃式的重新散列技术:

①二次散列技术

②随机重新散列技术

③双重散列技术 使用两个散列函数来产生探索序列 Hi(x) = (H(x)+iH’(x))%B i=1,2,…,B-1 (其中H’(x)的值应与B互质)

· 计算平均查找长度(假设成功查找的概率为P,不成功为V-P)

哈希表等概率情况下查找成功和查找不成功的平均查找长度的计算

作业题

9.1 hash



(k、m较小,视作m进制的哈希映射)

#include<cstdio>
#include<iostream>
#define MAX 1000000 + 7
#define HashMAX 6*6*6*6*6*6 + 7
using namespace std;

char str[MAX];
int i;
bool hash[HashMAX];

int main()
{
int n,m,k;
scanf("%d %d %d",&n,&m,&k);
scanf("%s",str);

if(k > n)
{
printf("0\n");
return 0;
}
int ans = 0;

int a = 0;
for(i = k-1; i >=0; i--)
a = a*m+(str[i]-'a');
hash[a] = true;
ans ++;

int val = 1; //第k个位置权重
i = 0;
while(++i < k) val*=m;

for(i = k; i < n; i++)
{
a = a/m + (str[i]-'a')*val;
if(!hash[a])
{
ans++;
hash[a] = true;
}
}

printf("%d",ans);

return 0;
}


9.2 寻人启事





(这里用了一个网络上找到的字符串哈希函数,冲突用链表解决)

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAX 1000000+7
using namespace std;
int i;

struct Person
{
char name[6];
Person * next;
}*per[MAX];

int getHash(char str[6])
{
int h = 5381;
int i;
for(i = 0; i < strlen(str); i++)
h = (((h<<5)+h)+str[i])%(MAX);
return h;
}

char c[6];

int main()
{
memset(per,0,sizeof(per));
Person *tmp;
int n,m;
scanf("%d%d",&n,&m);
for(i = 0; i < n; i++)
{
scanf("%s",c);
int hash = getHash(c);
tmp = new Person;
strcpy(tmp->name,c);
tmp->next = per[hash];
per[hash] = tmp;
}

int ans = 0;
for(i = 0; i < m; i++)
{
scanf("%s",c);
tmp = per[getHash(c)];
while(tmp)
{
if(strcmp(c,tmp->name)==0)
{
ans++;
break;
}
tmp = tmp->next;
}
}
printf("%d\n",ans);

return 0;
}


9.3 学生管理





(纯链表模拟的哈希,链表调了不知道多久,错因在于在函数中开了指针指向哈希串中的某个位置然后delete掉这个指针,这样哈希串那个位置就被delete掉了…(我还以为知道释放掉指针地址原来是释放指向的地址啊Orz))

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define MAX 100000+7
int i,j;
char s[6];
struct Student
{
char name[6];
Student * next;
}*stu[MAX];

int getHash(char str[6])
{
int h = 5381;
int len = strlen(str);
for(int i = 0; i < len; i++)
h = (((h<<5)+h)+str[i])%(MAX);
return h;
}

bool qur(char str[6])
{
int h = getHash(str);

if(!stu[h]->next) return 0;

Student * tmp = new Student;
Student * tmp1 = new Student;
tmp->next = NULL;
tmp1->next = NULL;

tmp = stu[h];
while(tmp->next)
{
tmp1 = tmp->next;
if(strcmp(tmp1->name,str) == 0) return 1;
tmp = tmp->next;
}
return 0;
}

void add(char str[6])
{
int h = getHash(str);
Student * tmp = new Student;
tmp->next = NULL;
strcpy(tmp->name,str);
if(!qur(tmp->name))
{
tmp->next = stu[h]->next;
stu[h]->next = tmp;
}
return;
}

void del(char str[6])
{
int h = getHash(str);

if(stu[h]->next == NULL)
{
printf("The student does not exist!\n");
return;
}

Student * tmp = new Student;
Student * tmp1 = new Student;
tmp = stu[h];
while(tmp->next)
{
tmp1 = tmp->next;
if(strcmp(tmp1->name,str) == 0)
{
tmp->next = tmp1->next;
delete(tmp1);
tmp1 = NULL;
return;
}
tmp = tmp->next;
}
printf("The student does not exist!\n");
return;
}

int main()
{
Student * tmp = new Student;
tmp->next = NULL;
for(i = 0; i < MAX; i++)
{
stu[i] = new Student;
stu[i]->next = NULL;
}

int n,op;
scanf("%d",&n);
for(i = 0; i < n; i++)
{
scanf("%d%s",&op,s);
switch(op)
{
case 1:
add(s);
break;
case 2:
del(s);
break;
case 3:
{
if(qur(s) == 1) printf("Yes\n");
else printf("No\n");
break;
}
}
}
return 0;
}


9.4 有趣的方程



(奇怪的题目..三个for..楼下是泡泡打的..if没考虑全还wa了一次..)

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
///----------------------------------------------
int main() {
int tcase=0;
int icase=0;
for (scanf("%d",&tcase); ++icase<=tcase; ) {

//--0 init

//--1 read
int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d);

//--2 work
int ans=0;
for (int x1=-100; x1<=100; x1++) if (x1)
for (int x2=-100; x2<=100; x2++) if (x2)
for (int x3=-100; x3<=100; x3++) if (x3) {
int tmp=a*x1*x1+b*x2*x2+c*x3*x3;
//tmp==-d*x4*x4
int tmp2=tmp/-d;
if (tmp2*-d!=tmp) continue;
if (tmp2<=0) continue;
int x4=sqrt(tmp2);
if (x4*x4!=tmp2) continue;
if (x4>100) continue;
ans+=2;
}

//--3 print
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: