数据结构---散列

原创 2016年08月31日 10:09:51

散列

此处还未补充关于散列知识

整型关键字的散列映射   (25分)

给定一系列整型关键字和素数PPP,用除留余数法定义的散列函数将关键字映射到长度为PPP的散列表中。用线性探测法解决冲突。

输入格式:

输入第一行首先给出两个正整数NNN≤1000\le 10001000)和PPP≥N\ge NN的最小素数),分别为待插入的关键字总数、以及散列表的长度。第二行给出NNN个整型关键字。数字间以空格分隔。

输出格式:

在一行内输出每个整型关键字在散列表中的位置。数字间以空格分隔,但行末尾不得有多余空格。

输入样例:

4 5
24 15 61 88

输出样例:

4 0 1 3

解题思路:

按照题中所说的散列函数以及冲突解决方式解题。

注意相同关键字时的处理。

提交代码:

编译器:g++

#include <iostream>
#include <stdio.h>
using namespace std;
const int MAXN = 1009;
const int INF = -(1<<30);
long long num[MAXN], List[MAXN];
bool Index[MAXN] = {false};
int main()
{
	int n, p;
	scanf("%d%d",&n,&p);
	for(int i = 0; i < p; ++i)
		List[i] = INF;
	for(int i = 0; i < n; ++i)
	{
		scanf("%d",&num[i]);
		int index = num[i] % p;
		if(index < 0) index = (index + p) % p; //除留余数法
		for(int j = 0; Index[index] && List[index] != num[i]; ++j) //该循环是线性探测
			index = (index + 1) % p;
		List[index] = num[i];
		Index[index] = true;
		num[i] = index;
	}
	for(int i = 0; i < n; ++i)
	{
		if(i) printf(" ");
		printf("%d", num[i]);
	}
	return 0;
} 

字符串关键字的散列映射   (25分)

给定一系列由大写英文字母组成的字符串关键字和素数PPP,用移位法定义的散列函数H(Key)H(Key)H(Key)将关键字KeyKeyKey中的最后3个字符映射为整数,每个字符占5位;再用除留余数法将整数映射到长度为PPP的散列表中。例如将字符串AZDEG插入长度为1009的散列表中,我们首先将26个大写英文字母顺序映射到整数0~25;再通过移位将其映射为3×322+4×32+6=32063\times 32^2 + 4 \times 32 + 6 = 32063×322+4×32+6=3206;然后根据表长得到,即是该字符串的散列映射位置。

发生冲突时请用平方探测法解决。

输入格式:

输入第一行首先给出两个正整数NNN≤500\le 500500)和PPP≥2N\ge 2N2N的最小素数),分别为待插入的关键字总数、以及散列表的长度。第二行给出NNN个字符串关键字,每个长度不超过8位,其间以空格分隔。

输出格式:

在一行内输出每个字符串关键字在散列表中的位置。数字间以空格分隔,但行末尾不得有多余空格。

输入样例1:

4 11
HELLO ANNK ZOE LOLI

输出样例1:

3 10 4 0

输入样例2:

6 11
LLO ANNA NNK ZOJ INNK AAA

输出样例2:

3 0 10 9 6 1

解题思路:

按照题中例子所说,模拟即可。

注意平方探测法,索引值出现负数时的情况以及关键字相同的情况

提交代码:

编译器:g++

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 1009;
struct infi{
	char str[9];
	bool ishave;	
}Hash[MAXN];
int location[MAXN];
int Move[3] = {0, 5, 10};
int main()
{
	int n, p;
	char str[8];
	scanf("%d%d", &n, &p);
	for(int i = 0; i < n; ++i)
	{
		scanf("%s", str);
		int len = strlen(str);
		int key = 0;
		for(int j = 0; j < 3 && len - j - 1 >= 0; ++j) //映射到一个数字
		{
			key += (str[len - j - 1] - 'A') * (1 << Move[j]);
		}
		key = key % p; //除留余数法
		for(int j = 0; true; ++j) //平方探测法
		{
			int di = (j * j) % p; //系数
			int tmpkey = (key + di) %p; //正方向探测
			if(!Hash[tmpkey].ishave)
			{
				location[i] = tmpkey, Hash[tmpkey].ishave = true;
				strcpy(Hash[tmpkey].str, str);
				break;
			}
			else if(strcmp(Hash[tmpkey].str, str) == 0)
			{
				location[i] = tmpkey;
				break;
			}
			tmpkey = (key - di) % p; //负方向探测
			if(tmpkey < 0) tmpkey = (tmpkey + p) % p;
			if(!Hash[tmpkey].ishave)
			{
				location[i] = tmpkey, Hash[tmpkey].ishave = true;
				strcpy(Hash[tmpkey].str, str);
				break;
			}
			else if(strcmp(Hash[tmpkey].str, str) == 0)
			{
				location[i] = tmpkey;
				break;
			}
		}
	}
	for(int i = 0; i < n; ++i)
	{
		if(i) printf(" ");
		printf("%d", location[i]);
	}
	return 0;
}

电话聊天狂人   (25分)

给定大量手机用户通话记录,找出其中通话次数最多的聊天狂人。

输入格式:

输入首先给出正整数NNN≤105\le 10^5105),为通话记录条数。随后NNN行,每行给出一条通话记录。简单起见,这里只列出拨出方和接收方的11位数字构成的手机号码,其中以空格分隔。

输出格式:

在一行中给出聊天狂人的手机号码及其通话次数,其间以空格分隔。如果这样的人不唯一,则输出狂人中最小的号码及其通话次数,并且附加给出并列狂人的人数。

输入样例:

4
13005711862 13588625832
13505711862 13088625832
13588625832 18087925832
15005713862 13588625832

输出样例:

13588625832 3

解题思路:

用除留余数法定义散列函数。

此处使用拉链法解决冲突,因此使用数组指针。

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 100003;
typedef struct infi node;
struct infi{
    long long num;
    int cnt;
    node * next;
};
node *L[MAXN];
int Find(long long n);
int main()
{
    int n;
    int peo = 0, cnt = 0;
    long long n1, n2, Num = 0;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    {
        scanf("%lld %lld", &n1, &n2);
        int cnt1 = Find(n1);
        int cnt2 = Find(n2); //散列操作
        if(cnt1 < cnt2) //一下两个if语句确定最大值
            n1 = n2, cnt1 = cnt2;
        if(cnt1 > cnt)
            cnt = cnt1, Num = n1;
    }
    for(int i = 0; i < MAXN; ++i)
    {
        while(L[i])
        {
            if(L[i]->cnt == cnt)
            {
                peo++;
                if(Num > L[i]->num)
                    Num = L[i]->num;
            }
            node *tmp = L[i];
            L[i] = L[i]->next;
            delete tmp;
        }
    }
    if(peo <= 1) printf("%lld %d\n", Num, cnt);
    else printf("%lld %d %d\n", Num, cnt, peo);
    return 0;
}
int Find(long long n)
{
    int index = n % MAXN, cnt = 0;
    node *tmp = L[index];
    if(tmp == NULL)
    {
        tmp = new node;
        tmp->next = NULL;
        tmp->cnt = 1;
        tmp->num = n;
        L[index] = tmp;
        return 1;
    }
    while(tmp->next != NULL && tmp->num != n)
    {
        tmp = tmp->next;
    }
    if(tmp == NULL)
    {
        node *p = new node;
        p->num = n;
        p->next = NULL;
        p->cnt = 1;
        tmp->next = p;
        cnt = 1;
    }
    else if(tmp->num == n)
    {
        tmp->cnt++;
        cnt = tmp->cnt;
    }
    return cnt;
}

QQ帐户的申请与登陆   (25分)

实现QQ新帐户申请和老帐户登陆的简化版功能。最大挑战是:据说现在的QQ号码已经有10位数了。

输入格式:

输入首先给出一个正整数NNN≤105\le 10^5105),随后给出NNN行指令。每行指令的格式为:“命令符(空格)QQ号码(空格)密码”。其中命令符为“N”(代表New)时表示要新申请一个QQ号,后面是新帐户的号码和密码;命令符为“L”(代表Login)时表示是老帐户登陆,后面是登陆信息。QQ号码为一个不超过10位、但大于1000(据说QQ老总的号码是1001)的整数。密码为不小于6位、不超过16位、且不包含空格的字符串。

输出格式:

针对每条指令,给出相应的信息:

1)若新申请帐户成功,则输出“New: OK”;
2)若新申请的号码已经存在,则输出“ERROR: Exist”;
3)若老帐户登陆成功,则输出“Login: OK”;
4)若老帐户QQ号码不存在,则输出“ERROR: Not Exist”;
5)若老帐户密码错误,则输出“ERROR: Wrong PW”。

输入样例:

5
L 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
L 1234567890 myQQ@qq
L 1234567890 myQQ@qq.com

输出样例:

ERROR: Not Exist
New: OK
ERROR: Exist
ERROR: Wrong PW
Login: OK

解题思路:

本题参照上题思路,

具体还要考虑题中的L操作,对应可能有三种结果

题中的N操作,对应有两种结果。

提交代码:

编译器:g++

#include <iostream>
#include <string.h>
using namespace std;
const int MOD = 100005;
typedef struct infi info;
struct infi{
	long long ID;
	char pass[21];
	info *next;
};
infi *count[MOD];
int cnt[MOD];
int main()
{
	int n;
	char com[3], pw[15];
	long long num;
	scanf("%d", &n);
	for(int i = 0; i < n; ++i)
	{
		scanf("%s %lld %s", com, &num, pw);
		int index = num % MOD; //除留余数法
		if(com[0] == 'L')
		{
			info *tmp = count[index];
			bool isexit = false;
			bool islaw = true;
			while(tmp && !isexit) //查找是否存在账号
			{
				if(tmp->ID == num) //检验用户名
				{
					isexit = true;
					if(strcmp(tmp->pass, pw) != 0) //判断密码是否正确
						islaw = false;
				}
				tmp = tmp->next;
			}
			if(!isexit) printf("ERROR: Not Exist\n"); //账号不存在
			else
			{
				if(islaw) printf("Login: OK\n"); //账号密码都正确
				else printf("ERROR: Wrong PW\n"); //账号正确,密码错误
			}
		}
		else
		{
			info *tmp = count[index];
			bool isexit = false;
			while(tmp && !isexit) //查找账号
			{
				if(tmp->ID == num)
					isexit = true;
				tmp = tmp->next;
			}
			if(isexit) printf("ERROR: Exist\n");
			else //新建账号
			{
				info *p = new info;
				p->ID = num;
				strcpy(p->pass, pw);
				p->next = NULL;
				if(cnt[index])
				{
					tmp = count[index];
					while(tmp->next)
						tmp = tmp->next;
					tmp->next = p;
				}
				else
					count[index] = p;
				cnt[index]++;
				printf("New: OK\n");
			}
		}
        }
	return 0;
}

航空公司VIP客户查询   (25分)

不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达到一定数量后,可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航空公司全体会员的飞行记录,要求实现根据身份证号码快速查询会员里程积分的功能。

输入格式:

输入首先给出两个正整数NNN≤105\le 10^5105)和KKK≤500\le 500500)。其中KKK是最低里程,即为照顾乘坐短程航班的会员,航空公司还会将航程低于KKK公里的航班也按KKK公里累积。随后NNN行,每行给出一条飞行记录。飞行记录的输入格式为:18位身份证号码(空格)飞行里程。其中身份证号码由17位数字加最后一位校验码组成,校验码的取值范围为0~9和x共11个符号;飞行里程单位为公里,是(0, 15 000]区间内的整数。然后给出一个正整数MMM≤105\le 10^5105),随后给出MMM行查询人的身份证号码。

输出格式:

对每个查询人,给出其当前的里程累积值。如果该人不是会员,则输出No Info。每个查询结果占一行。

输入样例:

4 500
330106199010080419 499
110108198403100012 15000
120104195510156021 800
330106199010080419 1
4
120104195510156021
110108198403100012
330106199010080419
33010619901008041x

输出样例:

800
15000
1000
No Info

解题思路:

此题中将身份证作为关键字,将身份证编号转化为11进制的数字(X代表10)

然后用除留余数法定义散列函数,用拉链法解决冲突。

提交代码:

编译器:g++

#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int MAXN = 100009;
typedef struct infi info;
struct infi{
    char ID[21];
    int totKM;
    info *next;
};
info peo[MAXN];
long long getIndex(char *str); //将身份证映射为数字
int main()
{
    int n, k, m;
    int KM;
    char ID[21];
    scanf("%d %d", &n, &k);
    for(int i = 0; i < n; ++i)
    {
        scanf("%s %d", ID, &KM);
        if(KM < k) KM = k;
        int index = (int) (getIndex(ID) % MAXN); //除留余数法
        if(peo[index].next == NULL) //拉链法
        {
            info *tmp = new info;
            tmp->next = NULL;
            strcpy(tmp->ID, ID);
            tmp->totKM = KM;
            peo[index].next = tmp;
        }
        else
        {
            info *tmp = peo[index].next;
            while(strcmp(tmp->ID, ID) && tmp->next)
                tmp = tmp->next;
            if(strcmp(tmp->ID, ID) == 0)
                tmp->totKM += KM;
            else
            {
                info *p = new info;
                p->next = NULL;
                p->totKM = KM;
                strcpy(p->ID, ID);
                tmp->next = p;
            }
        }
    }
    scanf("%d", &m); //查找
    for(int i = 0; i < m; ++i)
    {
        scanf("%s", ID);
        int index = (int)(getIndex(ID) % MAXN);
        info *tmp = peo[index].next;
        while(tmp)
        {
        	if(strcmp(tmp->ID, ID) == 0)
                break;
            tmp = tmp->next;
		}
        if(tmp) printf("%d\n", tmp->totKM);
        else printf("No Info\n");
    }
    return 0;
}
long long getIndex(char *str)
{
    long long num = 0;
    for(int i = 0; i < 17; ++i)
        num = num * 10 + (str[i] - '0');
    if(str[17] != 'x') num += (str[17] - '0');
    else num += 10;
    return num;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

航空公司VIP客户查询

5-12 航空公司VIP客户查询   (25分) 不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达到一定数量后,可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航空公...

5-45 航空公司VIP客户查询 (25分)

5-45 航空公司VIP客户查询 (25分)不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达到一定数量后,可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航空公司全体会员的飞行...

航空公司VIP客户查询【PAT】

航空公司VIP客户查询 典型的Hash操作

PTA 7-15(查找) 航空公司VIP客户查询(25 分) 25分代码 哈希方法

本来用map 写的,死活超时,看来卡不过去 只能哈希了 这里还稍微优化了一下,其实每个 哈希后的值存个 vector 来表示他的原值也行 (注: 博客作为交流使用,请勿抄袭应付作业) ...
  • xiang_6
  • xiang_6
  • 2017年10月19日 16:15
  • 160

7-15(查找) 航空公司VIP客户查询(25 分)

7-15(查找) 航空公司VIP客户查询(25 分) 不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达到一定数量后,可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航...

PTA-数据结构 5-45 航空公司VIP客户查询 (25分)

不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达到一定数量后,可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航空公司全体会员的飞行记录,要求实现根据身份证号码快速查询会员里程积分...
  • flx413
  • flx413
  • 2016年12月03日 12:57
  • 618

航空公司VIP客户查询

Think: 用STL水的,开一个MAP《string, int》来存储,输入的时候判断里程len是否大于给定的k值, 输出时判断是否为0。不少航空公司都会提供优惠的会员服务,当某顾客飞行里程累积达...

5-42 整型关键字的散列映射 (25分)

5-42 整型关键字的散列映射 (25分)给定一系列整型关键字和素数P,用除留余数法定义的散列函数将关键字映射到长度为P的散列表中。用线性探测法解决冲突。 输入格式:输入第一行首先给出两个正整数...

5-43 字符串关键字的散列映射 (25分)

5-43 字符串关键字的散列映射 (25分)给定一系列由大写英文字母组成的字符串关键字和素数P,用移位法定义的散列函数H(Key)将关键字Key中的最后3个字符映射为整数,每个字符占5位;再用除留...

《数据结构学习与实验指导》5-1:整数关键字的散列映射

实验内容:给定一系列整型关键字和素数PP,用除留余数法定义的散列函数将关键字映射到长度为PP的散列表中。用线性探测法解决冲突。 输入格式:输入第一行首先给出两个正整数N(N≤1000)和P(≥N的最...
  • henuni
  • henuni
  • 2017年07月23日 11:21
  • 158
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:数据结构---散列
举报原因:
原因补充:

(最多只允许输入30个字)