7-14 电话聊天狂人(25 分)(Hash表基本操作)
7-14 电话聊天狂人(25 分)
给定大量手机用户通话记录,找出其中通话次数最多的聊天狂人。
输入格式:
输入首先给出正整数N(≤105),为通话记录条数。随后N行,每行给出一条通话记录。简单起见,这里只列出拨出方和接收方的11位数字构成的手机号码,其中以空格分隔。
输出格式:
在一行中给出聊天狂人的手机号码及其通话次数,其间以空格分隔。如果这样的人不唯一,则输出狂人中最小的号码及其通话次数,并且附加给出并列狂人的人数。
输入样例:
4
13005711862 13588625832
13505711862 13088625832
13588625832 18087925832
15005713862 13588625832
输出样例:
13588625832 3
我觉得这道题用Hash表做还是有点烦的,特意跑去看了浙大慕课的视频
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 #define KEYLENGTH 12 //关键词字符串最大长度 6 #define MAXTABLESIZE 1000000 //允许开辟的最大散列表长度 7 #define MAXD 5 //参与散列映射计算的字符个数 8 typedef char ElementType[KEYLENGTH ]; 9 typedef int Index; //散列地址类型 10 11 //单链表定义 12 typedef struct LNode *PtrToLNode; 13 struct LNode { 14 ElementType Data; 15 PtrToLNode Next; 16 int Count; 17 }; 18 typedef PtrToLNode Position; 19 typedef PtrToLNode List; 20 21 //散列表结点定义 22 typedef struct TblNode *HashTable; //散列表类型 23 struct TblNode { 24 int TableSize; //表的最大长度 25 List Heads; //指向链表头结点的数组 26 }; 27 28 int NextPrime( int N ) { 29 //返回大于N且不超过MAXTABLESIZE的最小素数 30 int i, p = ( N % 2 ) ? N + 2 : N + 1; //从大于N的下一个奇数开始 31 while ( p <= MAXTABLESIZE ) { 32 double q = p; 33 for ( i = (int)sqrt(q); i > 2; i-- ) 34 if ( !(p % i) ) break; //p不是素数 35 if ( i == 2 ) break; // for正常结束,说明p是素数 36 else p += 2; //否则试探下一个奇数 37 } 38 return p; 39 } 40 41 HashTable CreateTable ( int TableSize ) { 42 HashTable H; 43 int i; 44 H = (HashTable)malloc(sizeof(struct TblNode)); 45 H->TableSize = NextPrime(TableSize); //保证散列表最大长度是素数 46 //以下分配链表头结点数组 47 H->Heads = (List)malloc(H->TableSize * sizeof(struct LNode)); //分配表头结点数组空间 48 for ( i = 0; i < H->TableSize; i++ ) { //初始化表头结点 49 H->Heads[i].Data[0] = '\0'; 50 H->Heads[i].Next = NULL; //链表为NULL 51 H->Heads[i].Count = 0; //对应号码的个数为0 52 } 53 return H; //最后将表头结点数组的首地址返回 54 } 55 56 //hash函数 57 Index Hash ( const char *Key, int TableSize ) { 58 unsigned int h = 0; //散列函数值,初始化为0 59 while ( *Key != '\0' ) //位移映射 60 h = ( h << 5 ) + *Key++; //左移五位 61 return h % TableSize; 62 } 63 64 Position Find ( HashTable H, ElementType Key ) { 65 Position P; 66 Index Pos; 67 Pos = Hash( Key + KEYLENGTH - MAXD, H->TableSize ); //初始散列位置(利用hash函数快速定位) 68 P = H->Heads[Pos].Next; //从该链表的第1个结点开始 69 while ( P && strcmp( P->Data, Key ) ) //寻找是否有Key,退出的条件:P为NULL, 或者是找到了 70 P = P->Next; 71 72 return P; //此时P或者指向找到的结点,或者为NULL 73 } 74 75 int Insert ( HashTable H, ElementType Key ) { 76 Position P, NewCell; 77 Index Pos; 78 P = Find( H, Key ); //先定位:找到or找不到 79 if ( !P ) { //关键词未找到,可以插入(声明临时节点存储待插入的数据,然后利用hash函数找到对应的位置,之后是链表的头插法) 80 NewCell = (Position)malloc(sizeof(struct LNode)); 81 strcpy(NewCell->Data, Key); 82 NewCell->Count = 1; //个数+1 83 Pos = Hash( Key + KEYLENGTH - MAXD, H->TableSize ); //初始散列位置 84 //将NewCell插入为H->Heads[Pos]链表的第一个结点(头插法) 85 NewCell->Next = H->Heads[Pos].Next; 86 H->Heads[Pos].Next = NewCell; 87 88 return 1; 89 } 90 else { //关键词已存在 91 P->Count++; 92 return 0; 93 } 94 } 95 96 void DestroyTable( HashTable H ) { //释放空间 97 int i; 98 Position P, Tmp; 99 //释放每个链表的结点 100 for( i = 0; i < H->TableSize; i++ ) { 101 P = H->Heads[i].Next; 102 while ( P ) { //依次释放链表每一个元素的空间 103 Tmp = P->Next; 104 free( P ); 105 P = Tmp; 106 } 107 } 108 free( H->Heads ); //释放头结点数组 109 free( H ); //释放散列表头结点 110 } 111 112 void ScanAndOutput ( HashTable H ) { 113 int i, MaxCnt = 0, PCnt = 0; 114 ElementType MinPhone; 115 List Ptr; 116 MinPhone[0] = '\0'; 117 for ( i = 0; i < H->TableSize; i++ ) { //扫描链表 118 Ptr = H->Heads[i].Next; //从该链表的第1个结点开始 119 while ( Ptr ) { 120 //要找最大的通话次数 121 if ( Ptr->Count > MaxCnt ) { //更新最大通话次数 122 MaxCnt = Ptr->Count; 123 strcpy( MinPhone, Ptr->Data ); 124 PCnt = 1; 125 } 126 else if ( Ptr->Count == MaxCnt ) { 127 PCnt++; //狂人计数 128 if ( strcmp( MinPhone, Ptr->Data ) > 0 ) 129 strcpy( MinPhone, Ptr->Data ); //更新狂人的最小手机号码 130 } 131 Ptr = Ptr->Next; 132 } 133 } 134 printf("%s %d", MinPhone, MaxCnt); 135 if ( PCnt > 1 ) 136 printf(" %d", PCnt); 137 printf("\n"); 138 } 139 int main () { 140 int N, i; 141 ElementType Key; 142 HashTable H; 143 scanf("%d", &N); 144 H = CreateTable( N * 2 ); //创建一个散列表 145 for ( i = 0; i < N; i++ ) { 146 scanf("%s", Key); Insert( H, Key ); 147 scanf("%s", Key); Insert( H, Key ); 148 } 149 ScanAndOutput( H ); 150 DestroyTable( H ); 151 152 return 0; 153 }
在这个国度中,必须不停地奔跑,才能使你保持在原地。如果想要寻求突破,就要以两倍现在速度奔跑!