数据结构PTA习题:11-散列4 Hashing - Hard Version逆散列问题 (30分)——散列+拓扑排序

11-散列4 Hashing - Hard Version 逆散列问题 (30分)

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.

中文版题目:
给定长度为 N 的散列表,处理整数最常用的散列映射是 H(x)=x%N。如果我们决定用线性探测解决冲突问题,则给定一个顺序输入的整数序列后,我们可以很容易得到这些整数在散列表中的分布。例如我们将 1、2、3 顺序插入长度为 3 的散列表HT[]后,将得到HT[0]=3,HT[1]=1,HT[2]=2的结果。
但是现在要求解决的是“逆散列问题”,即给定整数在散列表中的分布,问这些整数是按什么顺序插入的?

输入格式:
输入的第一行是正整数 N(≤1000),为散列表的长度。第二行给出了 N 个整数,其间用空格分隔,每个整数在序列中的位置(第一个数位置为0)即是其在散列表中的位置,其中负数表示表中该位置没有元素。题目保证表中的非负整数是各不相同的。

输出格式:
按照插入的顺序输出这些整数,其间用空格分隔,行首尾不能有多余的空格。注意:对应同一种分布结果,插入顺序有可能不唯一。例如按照顺序 3、2、1 插入长度为 3 的散列表,我们会得到跟 1、2、3 顺序插入一样的结果。在此规定:当前的插入有多种选择时,必须选择最小的数字,这样就保证了最终输出结果的唯一性。

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

散列问题+拓扑排序。
拓扑排序中,存储入度=0的顶点的容器我使用的是数组。每次将所有入度=0的顶点中数据最小的输出,删除输出顶点在图中的所有出边后,若有新增的入度=0的顶点也存入数组。循环往复,直至所有的非负顶点都实现输出。

C语言实现:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define Infinity 65535
struct vertex
{
	int index;
	int data;
};
typedef struct vertex * Vertex;
struct graph
{
	int Ne;
	int Nv;
	int * * M;
};
typedef struct graph * Graph;
Graph CreateGraph(int N);
int main()
{
	int N;
	scanf("%d", &N);
	Vertex V;
	V = (Vertex)malloc(N * sizeof(struct vertex));
	Graph G;
	G = CreateGraph(N);
	int i, j;
	int t;
	int cnt = 0;//记录输入的非负元素个数
	for (i = 0; i < N; i++)//有向图,无权重
	{
		scanf("%d", &V[i].data);
		V[i].index = i;
		if (V[i].data < 0) { t = i; }
		else { t = V[i].data % N; cnt++; }
		while (t != i)
		{
			G->M[t][i] = 1;
			t++;
			if (t == N) { t = t - N; }
		}
	}
	int * Indegree;//顶点入度
	Indegree = (int *)malloc(N * sizeof(int));
	//入度初始化
	for (i = 0; i < N; i++)
	{
		Indegree[i] = 0;
	}
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			if (G->M[j][i] == 1)
			{
				Indegree[i]++;
			}
		}
	}
	int * d;//记录所有此刻入度=0的顶点下标
	d = (int *)malloc(N * sizeof(int));
	for (i = 0; i < N; i++)
	{
		d[i] = -1;
	}
	for (j = 0, i = 0; i < N; i++)//此刻入度=0的顶点下标存入数组d
	{
		if (Indegree[i] == 0)
		{
			d[j] = i;
			j++;
		}
	}
	int flag = 0;
	while (1)
	{
		int min = Infinity;
		int x;//入度=0的最小顶点的下标
		for (i = 0; i < N; i++)
		{
			if (min < 0 && V[d[i]].data>0)
			{
				min = V[d[i]].data;
				x = i;
			}
			else if (min >= 0 && min > V[d[i]].data&&V[d[i]].data > 0)
			{
				min = V[d[i]].data;
				x = i;
			}
		}
		if (flag == 0) { printf("%d", V[d[x]].data); flag = 1; }
		else { printf(" %d", V[d[x]].data); }
		cnt--;//每输出一个cnt--,直至cnt=0
		if (cnt == 0) { break; }
		V[d[x]].data = Infinity;
		for (i = 0; i < N; i++)//删除输出顶点的所有出边,若使某顶点入度=0,则将该顶点存入数组d
		{
			if (G->M[d[x]][i] == 1 && Indegree[i] > 0)
			{
				Indegree[i]--;
				if (Indegree[i] == 0)
				{
					d[j] = i;
					j++;
				}
			}
		}
	}
	return 0;
}
Graph CreateGraph(int N)
{
	int i, j;
	Graph G;
	G = (Graph)malloc(sizeof(struct graph));
	G->Ne = 0;
	G->Nv = N;
	G->M = (int * *)malloc(N * sizeof(int *));
	for (i = 0; i < N; i++)
	{
		G->M[i] = (int *)malloc(N * sizeof(int));
	}
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < N; j++)
		{
			G->M[i][j] = 0;
		}
	}
	return G;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值