数据结构---散列

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

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

数据结构:散列(hashing)

声明:本文为学习数据结构与算法分析(第三版) Clifford A.Shaffer 著的学习笔记,代码有参考该书的示例代码。 散列方法把关键码值映射到数组中的位置来访问记录这个过程称为散列(hashi...
  • u014613043
  • u014613043
  • 2016年02月26日 13:01
  • 2236

数据结构之哈希表二(用开散列法实现哈希表)

前面我们讲了哈希冲突的闭散列(开放定址法)的两种方法,现在我们来介绍另一种方法:开散列法。 通常,每个桶中的同义词子表都很短,设有n个关键码通过某一个散列函数,存放到散列表中的m个桶...
  • hj605635529
  • hj605635529
  • 2017年04月25日 12:18
  • 258

深入理解数据结构之散列表、散列、散列函数

前言                            笔者以前对散列是什么?哈希又是什么?何谓散列表?散列函数又是个什么东东比较的迷惑。                     通过看一些书...
  • kiritor
  • kiritor
  • 2013年06月12日 12:08
  • 5757

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

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

二次探测再散列散列表 源代码(数据结构)

/**********散列表**********/ #ifndef HashTable_ #define HashTable_   #include "Except.h"   template cla...
  • zhanghong056
  • zhanghong056
  • 2014年08月05日 13:45
  • 724

java实现简单的散列数据结构

利用除留余数法和分离链接法来实现散列数据结构
  • qq1004642027
  • qq1004642027
  • 2015年04月29日 15:56
  • 1067

什么是数据库散列存储?

网站在Web 2.0时代,时常面临迅速增加的访问量(这是好事情),但是我们的应用如何满足用户的访问需求,而且基本上我们看到的情况都是性能瓶颈都是在数据库上,这 个不怪数据库,毕竟要满足很大访问量确实对...
  • zhangqiang_accp
  • zhangqiang_accp
  • 2017年06月22日 14:32
  • 307

数据结构的几种存储方式

在计算机中,数据的存储结构可以采用如下四种方法来实现。 1、顺序存储方式:顺序存储方式就是在一块连续的存储区域一个接着一个的存放数据。顺序存储方式把逻辑上相邻的节点存储在物理位置撒花姑娘相邻的存储单...
  • ly_main
  • ly_main
  • 2016年03月25日 15:41
  • 6033

一步步带你深入理解数据结构系列--散列表

以下所说的有任何疑问或者有任何错误,欢迎评论,但不可能时刻都上着CSDN,所以可以用微博@或私信我。微博:halooooJeffrey 概述 散列方法以最基本的向量作为底层支撑结构,通过适...
  • u011465808
  • u011465808
  • 2014年08月17日 19:21
  • 648

数据结构——线性表——散列存储结构——哈希表知识点总结

散列(hashing)是一种重要的存储方法,也是一种常见的查找方法。 基本思想:以结点的关键字k为自变量,通过一个确定的函数关系f,计算出对应的函数值,吧这个函数值解释为结点的存储地址,将结点存入...
  • misayaaaaa
  • misayaaaaa
  • 2017年05月14日 10:08
  • 1720
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:数据结构---散列
举报原因:
原因补充:

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