数据结构(陈越)PAT练习题 第九周:散列查找

09-1. Hashing

时间限制
100 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
CHEN, Yue

The task of this problem is simple: insert a sequence of distinct positive integers into a hash table, and output the positions of the input numbers. The hash function is defined to be "H(key) = key % TSize" where TSize is the maximum size of the hash table. Quadratic probing (with positive increments only) is used to solve the collisions.

Note that the table size is better to be prime. If the maximum size given by the user is not prime, you must re-define the table size to be the smallest prime number which is larger than the size given by the user.

Input Specification:

Each input file contains one test case. For each case, the first line contains two positive numbers: MSize (<=104) and N (<=MSize) which are the user-defined table size and the number of input numbers, respectively. Then N distinct positive integers are given in the next line. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print the corresponding positions (index starts from 0) of the input numbers in one line. All the numbers in a line are separated by a space, and there must be no extra space at the end of the line. In case it is impossible to insert the number, print "-" instead.

Sample Input:
4 4
10 6 4 15
Sample Output:
0 1 4 -

这一题要求使用平方探测法的Hash,并且只往右探测。Hash表的长度为不小于 MSize 的最小质数。需要注意,1既不是质数也不是合数,所以如果输入了1,那么长度应该定义为2。在平方探测法中,即使有空位也不一定能让数插入进去,所以探测了一定次数后就得退出,否则有可能死循环。下面是完整的代码:

#include <iostream>
using namespace std;

bool isPrime( int n )
{
	if (n==1)
		return false;
	for (int i=2; i*i<=n; ++i)
	{
		if (n%i==0)
			return false;
	}
	return true;
}

int main()
{
	int MSize, N;
	cin >> MSize >> N;
	while (!isPrime(MSize)) 
		++MSize;
	int* a = new int [MSize];
	for (int i=0; i<MSize; ++i)
		a[i] = -1;
	int num, idx;
	for (int i=0; i<N; ++i)
	{
		cin >> num;
		idx = num%MSize;
		if (i!=0)
			cout << ' ';
		if (a[idx]!=-1)
		{
			int j, t;
			for (j=1; j<=MSize; ++j)
			{
				t = (idx+j*j) % MSize;
				if (a[t]==-1)
				{
					a[t] = 1;
					break;
				}
			}
			if (j>MSize)
				cout << '-';
			else
				cout << t;
		}
		else
		{
			a[idx] = 1;
			cout << idx;
		}
	}
	delete [] a;
	return 0;
}


09-2. QQ帐户的申请与登陆

时间限制
800 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard

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

输入格式说明:

输入首先给出一个正整数N(<=105),随后给出N行指令。每行指令的格式为:“命令符(空格)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”。

样例输入与输出:

序号 输入 输出
1
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

这一题用 Hash 表是很合适的,不过不用 Hash 也是可以的。在 Visual C++ 的STL中可以直接使用 hash_map,但是它在PAT上提交则会导致编译错误,看来g++不支持啊 T_T... 既然用不了 hash_map,那就直接用 map 好了。如果自己开个数组弄个 Hash 表也可以,应该也不难。QQ号码的长度可能有10位,所以我用了 long long 类型。下面是我的代码。

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
	int N;
	cin >> N;
	map<long long,string> hs;
	for (int round=0; round<N; ++round)
	{
		char cmd;
		long long num;
		string pswd;
		cin >> cmd >> num >> pswd;
		if (cmd=='N')
		{
			if (hs.find(num)==hs.end())
			{
				hs.insert(make_pair(num, pswd));
				cout << "New: OK\n";
			}
			else
				cout << "ERROR: Exist\n";
		}
		else  // cmd=='L'
		{
			if (hs.find(num)!=hs.end())
			{
				if (hs[num]==pswd)
					cout << "Login: OK\n";
				else
					cout << "ERROR: Wrong PW\n";
			}
			else
				cout << "ERROR: Not Exist\n";
		}
	}

	return 0;
}


09-3. Hashing - Hard Version

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
HE, Qinming

Given a hash table of size N, we can define a hash function H(x) = x%N. Suppose that the linear probing is used to solve collisions, we can easily obtain the status of the hash table with a given sequence of input numbers.

However, now you are asked to solve the reversed problem: reconstruct the input sequence from the given status of the hash table. Whenever there are multiple choices, the smallest number is always taken.

Input Specification:

Each input file contains one test case. For each test case, the first line contains a positive integer N (<=1000), which is the size of the hash table. The next line contains N integers, separated by a space. A negative integer represents an empty cell in the hash table. It is guaranteed that all the non-negative integers are distinct in the table.

Output Specification:

For each test case, print a line that contains the input sequence, with the numbers separated by a space. Notice that there must be no extra space at the end of each line.

Sample Input:
11
33 1 13 12 34 38 27 22 32 -1 21
Sample Output:
1 13 12 21 33 34 38 27 22 32

终于到了PAT的最后一题。姥姥说这一题出的非常妙。在散列表中,有几个数是最先插入进去,其它的数则必须在这几个数后面依次插入。所以可以先建一个图,把它们的先后关系表示出来,然后用拓扑排序搞定!它把两个看似无关的数据结构结合起来,是非常巧妙的一题。当然,像我这样的小白,自然想不到这么巧妙的方法。我的思路比较原始。散列表里插入的数据是按从小到大的顺序来的,所以我建了一个堆,每次把可以输出的数放入堆里。每次输出一个数后,就去检查它在散列表里右边的数,如果右边的那个数与它本应放的位置之间没有“障碍”时,说明它可以输出了,就把它放入堆里。依次类推,直到最后堆空了为止。

在STL的 queue 模版里提供了堆结构。为了能方便地知道某个数在散列表里的下标,我把这个数和它的下标放在同一个结构里以方便查找。下面是完整的代码:

#include <iostream>
#include <queue>
using namespace std;

struct node
{
	int idx;
	int num;
};

bool operator < (const node& n1, const node& n2)
{
	return n1.num>n2.num;
}

int main()
{
	int N, M;
	cin >> N;
	M = N;
	node* a = new node [N];
	bool* canPrint = new bool [N];
	priority_queue<node> heap;
	for (int i=0; i<N; ++i)
	{
		a[i].idx = i;
		cin >> a[i].num;
		if (a[i].num<0)
		{
			--M;
			canPrint[i] = false;
			continue;
		}
		if (a[i].num%N==i)
		{
			canPrint[i] = true;
			heap.push( a[i] );
		}
		else
			canPrint[i] = false;
	}

	for (int round=0; round<M;)
	{
		if (round!=0)
			cout << ' ';
		// 输出当前最小的可以输出的
		node tmp = heap.top();	heap.pop();
		int id = tmp.idx;
		cout << tmp.num;
		if (++round>=M)
			break;
		a[id].num = -1;
		// 找到它的右边那个元素
		++id;
		if (id>=N)
			id = 0;
		while (a[id].num<0)
		{
			++id;
			if (id>=N)
				id = 0;
		}
		if (canPrint[id]&&a[id].num>0 || !canPrint[id]&&a[id].num<0)
			continue;
		int dad = a[id].num%N;
		bool connected = true;
		int i;
		for (i=dad; i!=id; ++i)
		{
			if (i>=N)
			{
				i = 0;
				if (i==id)
					break;
			}
			if (!canPrint[i] || a[i].num>0)
			{
				connected = false;
				break;
			}
		}
		if (connected && !canPrint[i])
		{
			canPrint[i] = true;
			heap.push( a[i] );
		}
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值