【第21期】观点:人工智能到底用 GPU?还是用 FPGA?

数据结构---散列

原创 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;
}

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

相关文章推荐

数据结构之(一)Hash(散列)

最近一直在准备面试,借此机会把数据结构相关整理一下,方便自己和其他人查阅。 该系列第一篇为Hash,主要考察点相对集中,对研发和测试的面试来说深度要求也不算太高,因此主要整理Hash数据结构的相关知识...

C#数据结构和算法学习系列十二----散列HashTable类

散列是一种常见的存储数据的技术,按照这种方式可以非常迅速地插入和取回数据。散列所采用的数据结构被称为是散列表。尽管散列表提供了快速地插入、删除、以及取回数据的操作,但是诸如查找最大值或最小值这样的查找操作,散列表却无法执行地非常快。对于这类操作,其他数据结构会更适合。.NET 框架库提供了一种非常有用的处理散列表的类,即Hashtable 类。 A.散列。散列表数据结构是围绕数组设计的。虽然可以稍后根据需要增加数组的大小,但是数组是由第0 号元素一直到一些预定义尺寸的元素组成的。存储在数组内的每一个数据项都是基于一些数据块的,这被称为是关键字。为了把一个元素存储到散列表内

图解数据结构(5)——散列法及哈希表

转载自http://www.cppblog.com/guogangj/archive/2009/10/15/98699.html 数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,...

数据结构与Java集合框架读书笔记---散列类(hash)

了解HashMap原理对于日后的缓存机制多少有些认识。在网络中也有很多方面的帖子,但是很多都是轻描淡写,很少有把握的比较准确的信息,在这里试着不妨说解一二,本文并非原创,对本文有兴趣的读者可以看数据结构和Java集合框架,本文是对其第13章的总结,做了几年的开发,是时候总结一下了。 HashMap实现了Map接口,HashMap主要以键值(key-value)的方式来体现,笼统的说就是采用key值的哈希算法,外加取余最终获取索引,而这个索引可以认定是一种地址,既而把相应的value存储在地址指向内容中。这样说或许比较概念化,也可能复述不够清楚,来看列式更加清晰: int hash=
  • czxiyj
  • czxiyj
  • 2009-07-24 23:49
  • 1166

C#数据结构和算法学习系列十二----散列HashTable类

散列是一种常见的存储数据的技术,按照这种方式可以非常迅速地插入和取回数据。散列所采用的数据结构被称为是散列表。尽管散列表提供了快速地插入、删除、以及取回数据的操作,但是诸如查找最大值或最小值这样的查找...
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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