PTA Hashing - Hard Version

题干:

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,根据其排列计算出其输入顺序。
题目思路如下:
     思考哈希表内部元素的排列, 对于一个不在其键值对应位置的元素,其与键值对应位置中间隔的元素一定在该元素之前被输入。而对于一个在其原本位置的元素,其可以在任何时刻被输入。故而,各个元素之间形成了一个有向图。又由于保证不会冲突,各个元素本质上形成了有向无环图。
     示例中的有向无环图如下所示:

1
12
13
21
22
27
32
33
34
38

     而对于元素的输入序列,通过有向无环图,可以看出,只需要对其进行拓补排序即可。对于同时可以出栈的节点,进行排序,即可按照题目要求输出最小的值。在实际代码的撰写中,采用优先队列(最小堆)作为输出缓冲区,用于拓补排序的输出。

     AC代码如下所示,采用邻接表的方式实现图的表示,使用拓补排序对其图进行输出得到答案。
图的构建按照本文之前描述的方法,首先计算该元素的目标位置,即 x % N x\%N x%N,然后根据当前位置将其与目标位置之间的元素作为其前置节点即可。
     构建时的图与上图中的图不是完全相等的,是将当前的边与之前的边全部相连。读者也许可以对其进行改良,减少必要的边数,提高算法效率,但是笔者未对其进行实践。

1
12
13
#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";
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值