Problem Description
Given a hash table of size , we can define a hash function . 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 (≤1000), which is the size of the hash table. The next line contains 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
Solution
题意理解:已知开方地址法线性探测的哈希表及其内容,求各数据插入表的顺序。
一开始尝试用对每组度为零的队列内元素进行排序,debug 的时候发现完全错误,度为零的结点集合是动态更新的,静态排序显然不可取!
需要每次循环开始找到度为零的结点内最小的元素,每次都要找,不能偷懒。
de 了很久的 bug 终于过了。。。详解在后面。
完整代码(AC):
#include <stdio.h>
#include <stdlib.h>
int N, H[1000], G[1000][1000], Indegree[1000], first = 1;
void BuildGraph()
{
int i, j, hash;
for (i = 0; i < N; i++) {
Indegree[i] = -1;
for (j = 0; j < N; j++) {
if (i == j) G[i][j] = 1;
else G[i][j] = G[j][i] = 0;
}
}
for (i = 0; i < N; i++) {
if (H[i] > 0) {
j = H[i] % N;
Indegree[i] = 0;
while (i != j) {
if (H[j] > 0) {
Indegree[i]++;
G[j][i] = 1;
}
j++;
if (j >= N) j %= N;
}
}
}
}
int FindMin()
{
int i, Min = -1, Minitem = 65535;
for (i = 0; i < N; i++) {
if (Indegree[i] == 0 && H[i] < Minitem) {
Min = i;
Minitem = H[i];
}
}
if (Min != -1)
if (first) {
printf("%d", H[Min]);
first = 0;
}else
printf(" %d", H[Min]);
return Min;
}
void TopSort()
{
int i, j;
while (1) {
i = FindMin();
if (i == -1) break;
for (j = 0; j < N; j++) {
if (G[i][j]) {
--Indegree[j];
}
}
}
}
int main()
{
scanf("%d", &N);
for (int i = 0; i < N; i++) scanf("%d", &H[i]);
BuildGraph();
TopSort();
return 0;
}
现在回过头来梳理一下代码:
程序框架很简单:读入哈希表、建图、拓扑排序。
- 我们需要先把已经生成好的开放地址哈希表读进来。
- 选择用邻接矩阵存图,图结点的下标就是哈希中的下标。
- 还需要一个保存各结点入度的数组。
为了方便直接全声明为全局变量了。
1️⃣ 建图
Indegree[]初始化为 -1 ,这是一个不可能出现的值,易于判断,对于空出来的哈希表位置下标的入度就是 -1- 各边初始化为 0 ,对角线初始化为 1 ,便于后面拓扑排序的循环入度减一变为 -1 表示已经输出过这个数了
- 建图的时候遍历哈希表中每个元素
- 如果元素大于零就说明不为空,进入入度计算
- 用另一个变量
j记录哈希函数值,即如果不发生冲突该元素本来应该在的位置 - 从哈希函数值所指的位置开始,逐个线性探测,路过的元素如过大于零(即不为空)就给元素入度加一
- 注意在插入边的时候顺序不要弄错,是从 j 到 i 有条边
- 内循环更新 j 的时候,即线性探测时注意对 j 取模,不要超过哈希表表长
- 用另一个变量
- 如果元素小于零就直接跳过
2️⃣ 拓扑排序
- 进入循环
- 从当前入度为零的结点中找到最小的结点并输出
- 如果没找到,返回 -1 并退出循环,因为此时所有的入度都变为 -1 了
- 如果找到了,对该结点的后继结点入度数减一,表示该元素已经输出,懒惰删除该结点