PTA Hashing - Hard Version 思路分析及代码解析

一、前导

1. 需要掌握的知识

良好的逻辑分析能力 + Hash表 + 拓扑排序

2. 题目信息

2.1 题目来源:PTA / 拼题A
2.2 题目地址: 7-18 Hashing - Hard Version

二、解题思路分析

1. 题意理解

已知Hash表的插入结果(通过线性探测法解决冲突),据此推导出输入数字的插入顺序

输入数据

11  //Hash表大小
33 1 13 12 34 38 27 22 32 -1 21  //Hash表的插入结果

输出数据

1 13 12 21 33 34 38 27 22 32  //输入数字的插入顺序

2. 思路分析

2.1 先看4数字的Hash表,以便更快的找到规律:33%11=0, 1%11=1, 13%11=2, 12%11=1,12本该插在数字1的位置,现在数字 1 13 在它的前面,意味着数字1和数字13一定先于12插入。按照这个规律,Hash表的插入结果可以构建出一个有向图(建议自己画一下,以便加深理解)

11
33 1 13 12

在这里插入图片描述
2.2 每次一定是入度为0的结点(Hash后直接插入,没有发生冲突) 且 数值最小的数字先插入(Whenever there are multiple choices, the smallest number is always taken)
2.3 根据2.2的分析,因此使用拓扑排序:每次收集入度为0的结点并挑选出对应数值最小的数字,打印该数字,将与该结点有边关系的结点入度-1,重复上述操作,直到所有有效数字得到打印

三、具体实现

1. 弯路和bug

本题考察了两个知识点:Hash表、拓扑排序,把他们联系到一起还是比较难的

2. 代码框架

2.1 采用的数据结构

二维数组,通过邻接矩阵表示图

#define MAX 1001
int Graph[MAX][MAX];

2.2 程序主体框架

               程序伪码描述
int main()
{
	1.创建一个空的有向图
	2.根据录入的数据,向图中插入边并统计各结点的入度数据
	3.进行拓扑排序,相较一般拓扑排序,除满足入度为0的条件外,还需要找到入度为0中数值最小的数字
}

2.3 各分支函数 或 重点步骤

2.3.1 CreateGraph() 构建一个空图,将Hash表的表大小N抽象为N个点,从 0 至 N-1 编号

void CreateGraph()
{
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			Graph[i][j] = noEdge;
}

2.3.2 将N个数字存储在数组Value[ ]中,对数组Value[ ]中的每个数字 Hash表大小:如果 Value[i]非负数(意味着有效数字) 且 j!=i(意味着发生冲突) ,说明结点j一定在结点i的前面,结点 j i 之间构成了一条从 j 指向 i 的有向边

	for (int i = 0; i < N; i++)
	{
		int j = Value[i] % N;

		while (j!=i && Value[i]>Empty)
		{
			Graph[j][i] = 1; //构建有向图
			inDegree[i]++; //统计入度

			if (++j == N)
				j=0;
		}
	}

2.3.3 TopologySort() 拓扑排序,每次收集入度为0的结点 并 挑选出对应数值最小的数字(可以通过Value数组确定数字),打印该数字,将与该结点有边关系的结点入度-1,重复上述操作,直到全部有效数字被打印

void TopologySort()
{
	int a[MAX],count=0, HashPrinted=0; //数组a用于存储入度为0的有效数字, count记录数组a有效元素个数 ; HashPrinted记录已打印的数字个数
	int index=0; //index记录 被打印数字 对应的 Value数组的下标值(也就是图结点编号)

	bool flag = true; //控制空格的打印

	while (true)
	{
		count = 0;

		for (int i = 0; i < N; i++)
			if (!inDegree[i] && Value[i] > Empty) //所有入度为0的结点且对应的数字为有效值
			{
				a[count] = Value[i];
				count++;
			}

		sort(a, a + count); //找到入度为0的结点中最小值

		if (flag)
		{
			cout << a[0];
			flag = false;
			HashPrinted++;
		}	
		else
		{
			cout << " " << a[0];
			HashPrinted++;
		}
			

		if (HashPrinted == HashNumber) //HashNumber记录有效数字的总数
			break;

		 //获取对应的Value数组下标
		for (int i = 0; i < N; i++)
			if (a[0] == Value[i])
				index = i;

		Value[index] = Empty; //打印之后,擦除

		for (int i = 0; i < N; i++)
			if (Graph[index][i]) //打印之后,将相关结点的入度-1
				inDegree[i]--;

	}
	
}

3. 完整AC编码

如有建议或问题,欢迎留言

#include <iostream>
#include <algorithm> //sort()
using namespace std;

#define MAX 1001
#define Empty -1 //A negative integer represents an empty cell in the hash table
#define noEdge 0

int Graph[MAX][MAX]; //33 1 13 12
int inDegree[MAX];
int Value[MAX];

int N,HashNumber=0;

void CreateGraph();
void Init();
void TopologySort();

int main()
{
	int data;

	cin >> N;

	CreateGraph();
	Init();

	for (int i = 0; i < N; i++)
	{
		cin >> data;

		if (data > Empty) //非负数就是待插入的数字
			HashNumber++;

		Value[i] = data;
	}

	for (int i = 0; i < N; i++)
	{
		int j = Value[i] % N;

		while (j!=i && Value[i]>Empty)
		{
			Graph[j][i] = 1; //构建有向图
			inDegree[i]++; //统计入度

			if (++j == N)
				j=0;
		}
	}

	TopologySort();

	return 0;
}

void TopologySort()
{
	int a[MAX],count=0,HashPrinted=0; //数组a用于存储入度为0的数字, count记录其有效元素个数 ; HashPrinted记录已打印的数字
	int index=0; //index记录 被打印数字 对应的 Value数组的下标值 

	bool flag = true;

	while (true)
	{
		count = 0;

		for (int i = 0; i < N; i++)
			if (!inDegree[i] && Value[i] > Empty) //所有入度为0的结点 且 对应的数字为有效值
			{
				a[count] = Value[i];
				count++;
			}

		sort(a, a + count); //找到入度为0的结点中最小值

		if (flag)
		{
			cout << a[0];
			flag = false;
			HashPrinted++;
		}	
		else
		{
			cout << " " << a[0];
			HashPrinted++;
		}
			

		if (HashPrinted == HashNumber)
			break;

		 //获取对应的数组下标
		for (int i = 0; i < N; i++)
			if (a[0] == Value[i])
				index = i;

		Value[index] = Empty; //打印之后,擦除

		for (int i = 0; i < N; i++)
			if (Graph[index][i]) //打印之后,将相关结点的入度-1
				inDegree[i]--;

	}
	
}

void CreateGraph()
{
	for (int i = 0; i < N; i++) //构建一个空图,将==Hash表的表大小N==抽象为N个点,从 0 至 N-1 编号
		for (int j = 0; j < N; j++)
			Graph[i][j] = noEdge;
}

void Init()
{
	for (int i = 0; i < N; i++)
	{
		inDegree[i] = 0;
		Value[i] = Empty;
	}
				
}

四、参考

浙江大学 陈越、何钦铭老师主讲的数据结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值