11-散列3 QQ帐户的申请与登陆 分数 25 作者 DS课程组 单位 浙江大学
题目:
实现QQ新帐户申请和老帐户登陆的简化版功能。最大挑战是:据说现在的QQ号码已经有10位数了。
输入格式:
输入首先给出一个正整数N(≤105),随后给出N行指令。每行指令的格式为:“命令符(空格)QQ号码(空格)密码”。其中命令符为“N”(代表New)时表示要新申请一个QQ号,后面是新帐户的号码和密码;命令符为“L”(代表Login)时表示是老帐户登陆,后面是登陆信息。QQ号码为一个不超过10位、但大于1000(据说QQ老总的号码是1001)的整数。密码为不小于6位、不超过16位、且不包含空格的字符串。
输出格式:
针对每条指令,给出相应的信息:
1)若新申请帐户成功,则输出“New: OK”;
2)若新申请的号码已经存在,则输出“ERROR: Exist”;
3)若老帐户登陆成功,则输出“Login: OK”;
4)若老帐户QQ号码不存在,则输出“ERROR: Not Exist”;
5)若老帐户密码错误,则输出“ERROR: Wrong PW”。
输入样例:
5
L 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
L 1234567890 myQQ@qq
L 1234567890 myQQ@qq.com
输出样例:
ERROR: Not Exist
New: OK
ERROR: Exist
ERROR: Wrong PW
Login: OK
代码长度限制:16 KB 时间限制:1200 ms 内存限制:64 MB
题目解析:
本题本质考察对哈希表的查询与插入。在这里采用除留取余法构造散列函数,使用分离链接法解决散列冲突,关于分离链接法的理解如下图所示(图片来源于慕课浙大数据结构)。
此外,该题还需对哈希表查询或插入结果的多种情况进行讨论,以及对字符串的一些基本处理。
参考代码:
// 使用分离链表法
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<math.h>
# define TABLESIZE 100000 // 表长
# define MAXNAME 10 // 账号最大长度
# define PASSWORD 16 // 密码最大长度
# define HAVE 1 // 创建时账号已存在
# define NOTEXIST 2 // 登录时账号不存在
# define WRONGPW 3 // 登录时密码错误
# define OK 4 // 登录或注册成功
// 结点的结构
struct Node{
char Name[MAXNAME+1];
char Password[PASSWORD+1];
struct Node* Next;
};
typedef struct Node* PtrToNode;
// 采用分离链表法存储散列表
struct Table{
int Length; // 散列表的长度
PtrToNode* Array; // 散列表的数组
};
typedef struct Table* PtrToTable;
PtrToTable CreateTable();
int Hash(char Name[]);
int InsertTable(PtrToTable H, char Name[], char Password[]);
int Login(PtrToTable H, char Name[],char Password[]);
int main(){
// 指令条数
int N;
scanf("%d",&N);
int i;
// 创建一个散列表
PtrToTable H = CreateTable();
char Command[2],Name[MAXNAME+1],Password[PASSWORD+1];
for(i=0;i<N;i++){
scanf("%s",Command);
scanf("%s",Name);
scanf("%s",Password);
if(Command[0]=='N'){
// 创建新用户
int IRes = InsertTable(H,Name,Password);
if(IRes==HAVE)printf("ERROR: Exist\n");
else if(IRes==OK)printf("New: OK\n");
}else if(Command[0]=='L'){
// 登录
int LRes = Login(H,Name,Password);
if(LRes == NOTEXIST)printf("ERROR: Not Exist\n");
else if(LRes == WRONGPW)printf("ERROR: Wrong PW\n");
else if(LRes == OK)printf("Login: OK\n");
}
}
return 0;
}
// 创建并初始化一个散列表
PtrToTable CreateTable(){
PtrToTable H = (PtrToTable)malloc(sizeof(struct Table));
H->Length = TABLESIZE;
H->Array = (PtrToNode*)malloc(sizeof(PtrToNode)*H->Length);
// 每个位置处设置一个空表头
int i;
for(i=0;i<H->Length;i++){
PtrToNode New = (PtrToNode)malloc(sizeof(struct Node));
New->Next = NULL;
H->Array[i] = New;
}
return H;
}
// 散列函数
int Hash(char Name[]){
// 因为QQ号码最小为1001,因此最多只能利用前面4位
int i,res = 0;
for(i=0;i<=3;i++){
res = 10*res + Name[i] - '0';
}
res = res*(Name[3]-'0') % TABLESIZE;
return res;
}
// 用户登录
int Login(PtrToTable H, char Name[],char Password[]){
// 获取键值
int Key = Hash(Name);
// 跳过空表头
PtrToNode Tmp = H->Array[Key]->Next;
while(Tmp!=NULL){
if(strcmp(Tmp->Name, Name)==0){
// 账号匹配成功
if(strcmp(Tmp->Password,Password)==0){
// 账号密码均匹配成功,登录成功
return OK;
}else{
// 账号存在,但密码匹配失败
return WRONGPW;
}
}
Tmp = Tmp->Next;
}
// 账号未匹配上
return NOTEXIST;
}
// 用户注册
int InsertTable(PtrToTable H, char Name[], char Password[]){
// 获取键值
int Key = Hash(Name);
PtrToNode Last = H->Array[Key];
PtrToNode Tmp = Last -> Next;
while(Tmp!=NULL){
if(strcmp(Tmp->Name, Name) == 0){
// 账号已经存在
return HAVE;
}
Last = Tmp;
Tmp = Tmp->Next;
}
// 在while中没有结束说明不存在,则进行插入
PtrToNode New = (PtrToNode)malloc(sizeof(struct Node));
strcpy(New->Name, Name);
strcpy(New->Password,Password);
Last->Next = New;
New->Next = Tmp;
// 插入成功
return OK;
}