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