题干:
Given a hash table of size N N N, we can define a hash function H ( x ) = x % N H(x)=x\%N 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
题目分析:
首先阐述一下题目意思,简要概括而言,本题的意思是需要对一个给出的,按照线性探测算法进行排序的hash table,根据其排列计算出其输入顺序。
题目思路如下:
思考哈希表内部元素的排列, 对于一个不在其键值对应位置的元素,其与键值对应位置中间隔的元素一定在该元素之前被输入。而对于一个在其原本位置的元素,其可以在任何时刻被输入。故而,各个元素之间形成了一个有向图。又由于保证不会冲突,各个元素本质上形成了有向无环图。
示例中的有向无环图如下所示:
而对于元素的输入序列,通过有向无环图,可以看出,只需要对其进行拓补排序即可。对于同时可以出栈的节点,进行排序,即可按照题目要求输出最小的值。在实际代码的撰写中,采用优先队列(最小堆)作为输出缓冲区,用于拓补排序的输出。
AC代码如下所示,采用邻接表的方式实现图的表示,使用拓补排序对其图进行输出得到答案。
图的构建按照本文之前描述的方法,首先计算该元素的目标位置,即
x
%
N
x\%N
x%N,然后根据当前位置将其与目标位置之间的元素作为其前置节点即可。
构建时的图与上图中的图不是完全相等的,是将当前的边与之前的边全部相连。读者也许可以对其进行改良,减少必要的边数,提高算法效率,但是笔者未对其进行实践。
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<malloc.h>
#include<functional>
using namespace std;
// ListNode
typedef struct ListNode{
int targetNode;
ListNode* next;
}ListNode;
// 邻接表节点
typedef struct Node {
int num;
int index;
int indegree;
// 头指针与尾指针,尾指针用于直接插入
ListNode* nextNode;
ListNode* tailNode;
}Node;
Node* G;
void createG(int n);
void insertEdge(Node* from, Node* to);
void moveEdge(int index, int size);
void printG(int size); // function for test
int main() {
int n;
cin >> n;
int negNum=0;
// input hash table
priority_queue<int,vector<int>,greater<int> > q;
// graph for toposort
createG(n);
// init hash table && init graph
for (int i = 0; i < n; i++) {
cin >> G[i].num;
G[i].indegree = 0; G[i].index = i;
G[i].nextNode = NULL; G[i].tailNode = NULL;
}
// init graph
for (int i = 0; i < n; i++) {
int key = G[i].num % n;
if (G[i].num < 0) {
G[i].indegree = -1;
continue;
}
int length;
G[i].indegree = G[i].index>=key ? G[i].index-key : n-key+G[i].index;
length = G[i].indegree;
int j=0;
while (j < length) {
int k = key + j;
if (k >= n) k -= n;
if(G[k].num>=0)
insertEdge(&G[k], &G[i]);
else {
G[i].indegree--;
negNum++;
}
j++;
}
}
// TopoSort
// start find min zero degree
for (int i = 0; i < n; i++) {
if (G[i].indegree == 0 && G[i].num>=0){
q.push(G[i].num);
G[i].indegree = -1;
}
}
while (!q.empty()) {
int top = q.top();
for(int i=0;i<n;i++) {
if(G[i].num==top)
moveEdge(i, n);
}
q.pop();
// push into heap
for (int i = 0; i < n; i++) {
if (G[i].indegree == 0) {
q.push(G[i].num);
G[i].indegree = -1;
}
}
cout<<top;
if(!q.empty()) cout<<" ";
}
return 0;
}
void createG(int n) {
G = (Node*)malloc(sizeof(Node) * n);
if (!G) exit(1);
}
void insertEdge(Node* from, Node* to) {
ListNode* p = (ListNode*)malloc(sizeof(ListNode)*1);
if (p){
p->targetNode = to->index;
p->next = NULL;
if (from->nextNode == NULL){
from->nextNode = p;
from->tailNode = from->nextNode;
}else{
from->tailNode->next = p;
from->tailNode = from->tailNode->next;
}
}
else
cout << "P==NULL\n";
}
// delete node at index, adjust indegree
void moveEdge(int index, int size) {
ListNode* p = G[index].nextNode;
while (p!=NULL) {
G[p->targetNode].indegree--;
p = p->next;
}
G[index].num = -1; G[index].indegree = -1;
G[index].nextNode = NULL; G[index].tailNode = NULL;
}
void printG(int size) {
for (int i = 0; i < size; i++){
cout<< "num:" << G[i].num<<" index:"<<G[i].index<<" indegree:"<<G[i].indegree<<"\n";
ListNode* p=G[i].nextNode;
if (p == NULL) cout << "NULL";
while (p) {
cout << "targetNode:" << p->targetNode<<" ";
p = p->next;
}
cout << "\n";
}
}