散列(Hashing)的基本思想:
1.计算位置:构造散列函数确定关键词存储位置。
2.解决冲突:应用某种策略解决多个关键词位置相同的问题。
完整代码如下:
如下程序实现统计打电话次数最多的人
输入:通话记录的条数N。接下来输入N条记录,每条记录含有两个电话号码。
冲突解决办法:分离链接法。
散列函数:除留余数法。
//查找打电话最多的人
//冲突处理的方法:分离链接法
//哈希函数:出留余数法
//#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <math.h>
#include <stdlib.h>
using namespace std;
const int MAXD = 5; //取电话后5位为key
const int KEYLENGTH = 11; //电话号码的长度
const int MAXTABLESIZE = 10000; //允许开辟的最大散列表长度
//链表节点定义
struct LNode
{
char Data[KEYLENGTH + 1]; //数据域
struct LNode* Next; //指针域
int Count; //用来记录次数
};
typedef struct LNode* ListNode;
template <class T>
class Table
{
private:
struct LNode* Heads; //保存指向链表头结点的数组、类似于int* A;A - 数组A
int TableSize; //散列表的长度
public:
Table(int size); //构造函数
~Table(); //析构函数
int Hash(int KeyNum); //哈希函数
ListNode Find(char key[]); //散列查找
bool Insert(char key[]); //向散列表中插入数据
void Print(); //输出电话聊天狂人
int NextPrime(int N); //返回大于N的最小素数
};
//Table类的实现
//构造函数
template <class T> Table<T>::Table(int size)
{
char key[KEYLENGTH+1];
TableSize = NextPrime(size * 2);
Heads = new LNode[TableSize * sizeof(ListNode)]; //为链表数组分配空间
//散列表的初始化
for (int i = 0; i < TableSize; ++i)
{
Heads[i].Data[0] = '\0';
Heads[i].Count = 0;
Heads[i].Next = NULL;
}
//插入通话记录
for (int i = 0; i < size; i++)
{
cin >> key;
Insert(key);
}
}
//返回大于size的最小素数
template <class T> int Table<T>::NextPrime(int N)
{
int p, i;
//从大于N的第一个奇数开始
if (N % 2 == 0)
p = N + 1;
else
p = N + 2;
while (p <= MAXTABLESIZE)
{
for (i = (int)sqrt(p); i > 2; --i)
{
if (p%i == 0) //p不是素数,break
break;
}
if (i == 2) break; //说明p是素数,while循环结束
else
p += 2; //否则试探下一个奇数
}
return p;
}
//哈希函数
template <class T> int Table<T>::Hash(int KeyNum)
{
//除留余数法
return KeyNum % TableSize;
}
//散列查找
template <class T> ListNode Table<T>::Find(char key[])
{
int Pos;
ListNode p;
//计算要查找的key的哈希值
Pos = Hash(atoi(key + KEYLENGTH - MAXD));
//从该哈希值所对应的链表的第一个节点开始
p = Heads[Pos].Next;
//当未到该链表尾,并且key未找到时
while (p != NULL && strcmp(p->Data, key))
p = p->Next;
return p; //p为指向的节点或者NULL
}
//向散列表中插入数据
template <class T> bool Table<T>::Insert(char key[])
{
ListNode P, NewCell;
int Pos;
P = Find(key);
if (!P) //关键词未找到,则插入该新数据
{
NewCell = new LNode;
strcpy(NewCell->Data, key);
NewCell->Count = 1;
Pos = Hash(atoi(key + KEYLENGTH - MAXD));
//将NewCell插到Heads[Pos]链表的头
NewCell->Next = Heads[Pos].Next;
Heads[Pos].Next = NewCell;
return true;
}
else //关键词以存在
{
P->Count++;
return false;
}
}
//输出电话聊天狂人
template <class T> void Table<T>::Print()
{
ListNode Ptr;
int i, MaxCnt, PCnt;
MaxCnt = PCnt = 0; //MaxCnt - 最多次数,PCnt个聊天狂人
char MinPhone[KEYLENGTH+1];
MinPhone[0] = '\0';
for (i = 0; i < TableSize; i++)
{
Ptr = Heads[i].Next;
while (Ptr)
{
if (Ptr->Count > MaxCnt)
{
MaxCnt = Ptr->Count;
strcpy(MinPhone, Ptr->Data);
PCnt = 1;
}
else if (Ptr->Count == MaxCnt)
{
PCnt++; //通话狂人数加1
if (strcmp(MinPhone, Ptr->Data) > 0)
{
strcpy(MinPhone, Ptr->Data); //更新狂人的最小电话号码
}
}
Ptr = Ptr->Next;
}
}
cout << "电话:" << MinPhone << "最大通话次数:" << MaxCnt << endl;
if (PCnt > 1)
cout << "PCnt: " << PCnt << endl;
}
//析构函数,释放动态分配的内存空间
template <class T> Table<T>::~Table()
{
int i;
ListNode P, tmp;
for (i = 0; i < TableSize; ++i)
{
P = Heads[i].Next;
while (P)
{
tmp = P->Next;
delete tmp;
P = tmp;
}
}
}
int main()
{
int N;
cin >> N;
Table<char> H(N);
H.Print();
return 0;
}