PTA 电话聊天狂人 思路分析及代码解析

一、前导

1. 需要掌握的知识

通过分离链接法实现Hash/哈希表

2. 题目信息

2.1 题目来源:PTA / 拼题A
2.2 题目地址: 电话聊天狂人

二、解题思路分析

1. 题意理解

对输入的通话记录进行整理,找到出现次数最多的电话号码、通话次数;若不唯一,则需要找到最小号码、通话次数和并列人数

1. 1 输入数据

4 //通话记录条数
13005711862 13588625832  //具体的通话记录                                       
13505711862 13088625832
13588625832 18087925832
15005713862 13588625832

1.2 输出数据

13588625832 3 // 出现次数最多的电话号码 通话次数
......

2. 思路分析

与陈越老师讲解的思路一致:首先创建Hash表(链表方式),然后将录入的数据存储在Hash表中,最后扫描Hash表找到目标数据

三、具体实现

1. 弯路和bug

Hash表编码的熟练度低

2. 代码框架

2.1 采用的数据结构

结构体、链表

typedef char ElementType[KeyLength + 1]; //关键字类型是字符串 电话号码11位+'\0'

//链表定义
typedef struct LinkTableNode* PtrLinkTableNode;
typedef PtrLinkTableNode List;
struct LinkTableNode
{
	ElementType Data; //电话号码
	PtrLinkTableNode Next; //相同哈希值的下一个号码
	int Count; //Count用于指示电话号码出现的次数
};

//散列表结点定义
typedef struct HashTableNode* HashTable;
struct HashTableNode
{
	int HashTableSize;//表最大长度
	List Heads; //指向链表头结点的数组
};

2.2 程序主体框架

               程序伪码描述
int main()
{
	1.创建链表形式的Hash表	
	2.将电话号码插入到Hash表中
	3.扫描Hash表找到目标数据并打印
	4.释放Hash表占用的内存空间
}

2.3 各分支函数

2.3.1 CreateHashTable( ) 创建Hash表,属于基本功

HashTable CreateHashTable(int HashTableSize)
{
	HashTable H;
	H = (HashTable)malloc(sizeof(HashTableNode));
	H->HashTableSize = NextPrime(HashTableSize); //Hash表大小:大于存储元素个数的素数
	//链表头结点数组
	H->Heads = (List)malloc(sizeof(struct LinkTableNode) * H->HashTableSize);

	for (int i = 0; i < H->HashTableSize; i++) //初始化 链表头结点数组各数组元素
	{
		H->Heads[i].Next = NULL;
		H->Heads[i].Data[0] = '\0';
		H->Heads[i].Count = 0;//计数
	}

	return H;
}

2.3.2 Find( ) 在Hash表中搜索元素,若为空,返回Null;若找到,返回该链接点的位置。Find函数被Insert函数调用

Position Find(HashTable H, ElementType Key)
{
	Position P;
	Index Pos;

	Pos = Hash(Key, H->HashTableSize); //初始化散列位置:求余
//strcmp(str1,str2),若str1=str2,返回零;若str1<str2,返回负数;若str1>str2,返回正数
	P = H->Heads[Pos].Next;//从链表的第一个结点开始寻找
	while (P && strcmp(P->Data, Key))
		P = P->Next;

	return P;
}

2.3.3 Insert( ) 在Hash表中插入元素

bool Insert(HashTable H, ElementType Key)
{
	Position P, NewNode;
	Index Pos;

	P = Find(H, Key);
	if (!P) //P=NULL 关键词未找到,直接插入
	{
		NewNode = (Position)malloc(sizeof(struct LinkTableNode));
		strcpy(NewNode->Data, Key);
		NewNode->Count = 1;
		Pos = Hash(Key, H->HashTableSize); //初始化散列位置
		NewNode->Next = H->Heads[Pos].Next;//每次在头结点后增加,若插入顺序是1 2 3,链表顺序就是 head-3-2-1
		H->Heads[Pos].Next = NewNode;
		return true;
	}
	else
	{
		P->Count++;
		return false;
	}
}

2.3.4 ScanAndOutput( ) 扫描Hash表找到目标数据并打印

void ScanAndOutput(HashTable H)
{
	int MaxCount = 0, PeopleCount = 0; //PeopleCount代表结果不唯一时的并列人数
	ElementType MinPhoneNumber;
	List Ptr;
	MinPhoneNumber[0] = '\0'; 

	for (int i = 0; i < H->HashTableSize; i++)
	{
		Ptr = H->Heads[i].Next;
		while (Ptr)
		{
			if (Ptr->Count > MaxCount)
			{
				MaxCount = Ptr->Count;
				strcpy(MinPhoneNumber, Ptr->Data);
				PeopleCount = 1;
			}
			else if (Ptr->Count == MaxCount)
			{
				PeopleCount++;
				if (strcmp(MinPhoneNumber, Ptr->Data) > 0)//strcmp(str1,str2),若str1>str2,返回正数
					strcpy(MinPhoneNumber, Ptr->Data); //MinPhoneNumber=Ptr->Data
			}
			Ptr = Ptr->Next;
		}
	}

	cout << MinPhoneNumber << ' ' << MaxCount;
	if (PeopleCount > 1)
		cout << ' ' << PeopleCount << endl;

	return;
}

2.3.5 DestoryHashTable( ) 释放Hash表占用的内存空间

void DestoryHashTable(HashTable H)
{
	Position P, Tmp;

	/* 释放每个链表的结点 */
	for (int i = 0; i < H->HashTableSize; i++)
	{
		P = H->Heads[i].Next;
		while (P)
		{
			Tmp = P->Next;
			free(P);
			P = Tmp;
		}
	}
	free(H->Heads); /* 释放头结点数组 */
	free(H);        /* 释放散列表结点 */
}

2.3.6 NextPrime( ) 和 Hash( )属于基础编码,在此不单独列出

3. 完整AC编码

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

#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;

#define MAX 300000
#define KeyLength 11 //关键字字符串最大长度

typedef char ElementType[KeyLength + 1]; //关键字类型是字符串 11位+'\0'
typedef int Index;//散列地址类型

/*单链表定义*/
typedef struct LinkTableNode* PtrLinkTableNode;
typedef PtrLinkTableNode List;
typedef PtrLinkTableNode Position;
struct LinkTableNode
{
	ElementType Data;
	PtrLinkTableNode Next;
	int Count;
};

//散列表结点定义
typedef struct HashTableNode* HashTable;
struct HashTableNode
{
	int HashTableSize;//表最大长度
	List Heads; //指向链表头结点的数组
};


HashTable CreateHashTable(int HashTableSize);
Position Find(HashTable H, ElementType Key);
bool Insert(HashTable H, ElementType Key);
int NextPrime(int n);
int Hash(ElementType Key, int TableSize);
void ScanAndOutput(HashTable H);
void DestoryHashTable(HashTable H);

int main()
{
	int N; //N represent record number
	ElementType Key; //typedef char ElementType[KeyLength+1];

	HashTable H;//Define HashTable H

	cin >> N;
	H = CreateHashTable(N * 2);//Create a Hash Table

	for (int i = 0; i < N; i++)
	{
		cin >> Key; Insert(H, Key); //put Key in HashTable H
		cin >> Key; Insert(H, Key); // a record contain two number
	}

	ScanAndOutput(H);
	DestoryHashTable(H);

	return 0;
}

void DestoryHashTable(HashTable H)
{
	Position P, Tmp;

	/* 释放每个链表的结点 */
	for (int i = 0; i < H->HashTableSize; i++)
	{
		P = H->Heads[i].Next;
		while (P)
		{
			Tmp = P->Next;
			free(P);
			P = Tmp;
		}
	}
	free(H->Heads); /* 释放头结点数组 */
	free(H);        /* 释放散列表结点 */
}

void ScanAndOutput(HashTable H)
{
	int MaxCount = 0, PeopleCount = 0;
	ElementType MinPhoneNumber;
	List Ptr;
	MinPhoneNumber[0] = '\0'; //MinPhoneNumber[0]

	for (int i = 0; i < H->HashTableSize; i++)
	{
		Ptr = H->Heads[i].Next;
		while (Ptr)
		{
			if (Ptr->Count > MaxCount)
			{
				MaxCount = Ptr->Count;
				strcpy(MinPhoneNumber, Ptr->Data);
				PeopleCount = 1;
			}
			else if (Ptr->Count == MaxCount)
			{
				PeopleCount++;
				if (strcmp(MinPhoneNumber, Ptr->Data) > 0)
					strcpy(MinPhoneNumber, Ptr->Data);
			}
			Ptr = Ptr->Next;
		}
	}

	cout << MinPhoneNumber << ' ' << MaxCount;
	if (PeopleCount > 1)
		cout << ' ' << PeopleCount << endl;

	return;
}

HashTable CreateHashTable(int HashTableSize)
{
	HashTable H;
	H = (HashTable)malloc(sizeof(HashTableNode));
	H->HashTableSize = NextPrime(HashTableSize);

	//链表头结点数组
	H->Heads = (List)malloc(sizeof(struct LinkTableNode) * H->HashTableSize);

	for (int i = 0; i < H->HashTableSize; i++)
	{
		H->Heads[i].Next = NULL;
		H->Heads[i].Data[0] = '\0';
		H->Heads[i].Count = 0;//计数
	}

	return H;
}

Position Find(HashTable H, ElementType Key)
{
	Position P;
	Index Pos;

	Pos = Hash(Key, H->HashTableSize); //初始化散列位置

	P = H->Heads[Pos].Next;//从链表的第一个结点开始寻找
	while (P && strcmp(P->Data, Key))
		P = P->Next;

	return P;
}

bool Insert(HashTable H, ElementType Key)
{
	Position P, NewNode;
	Index Pos;

	P = Find(H, Key);
	if (!P) //P=NULL 关键词未找到,直接插入
	{
		NewNode = (Position)malloc(sizeof(struct LinkTableNode));
		strcpy(NewNode->Data, Key);
		NewNode->Count = 1;
		Pos = Hash(Key, H->HashTableSize); //初始化散列位置
		NewNode->Next = H->Heads[Pos].Next;//每次在头结点后增加,若插入顺序是1 2 3,链表顺序就是 head-3-2-1
		H->Heads[Pos].Next = NewNode;
		return true;
	}
	else
	{
		P->Count++;
		return false;
	}
}

int Hash(ElementType Key, int TableSize)
{
	int k;
	k = atoi(Key + KeyLength - 5); //ASCIItoInt,使用11位电话号码的后5位进行哈希
	return k % TableSize;
}

int NextPrime(int n)//返回大于n 且 不超过MAX的最小素数
{
	int p, i;
	p = (n % 2) ? n + 2 : n + 1; //p一定是奇数且比n大

	while (p < MAX)
	{
		for (i = (int)sqrt(p); i > 2; i--)
			if (!(p % i))
				break;

		if (i == 2) break;
		else
			p += 2;
	}
	return p;
}

四、参考

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

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值