散列
此處還未補充關於散列知識
整型關鍵字的散列映射 (25分)
給定一系列整型關鍵字和素數PPP,用除留餘數法定義的散列函數將關鍵字映射到長度爲PPP的散列表中。用線性探測法解決衝突。
輸入格式:
輸入第一行首先給出兩個正整數NNN(≤1000\le 1000≤1000)和PPP(≥N\ge N≥N的最小素數),分別爲待插入的關鍵字總數、以及散列表的長度。第二行給出NNN個整型關鍵字。數字間以空格分隔。
輸出格式:
在一行內輸出每個整型關鍵字在散列表中的位置。數字間以空格分隔,但行末尾不得有多餘空格。
輸入樣例:
4 5
24 15 61 88
輸出樣例:
4 0 1 3
解題思路:
按照題中所說的散列函數以及衝突解決方式解題。
注意相同關鍵字時的處理。
提交代碼:
編譯器:g++
#include <iostream>
#include <stdio.h>
using namespace std;
const int MAXN = 1009;
const int INF = -(1<<30);
long long num[MAXN], List[MAXN];
bool Index[MAXN] = {false};
int main()
{
int n, p;
scanf("%d%d",&n,&p);
for(int i = 0; i < p; ++i)
List[i] = INF;
for(int i = 0; i < n; ++i)
{
scanf("%d",&num[i]);
int index = num[i] % p;
if(index < 0) index = (index + p) % p; //除留餘數法
for(int j = 0; Index[index] && List[index] != num[i]; ++j) //該循環是線性探測
index = (index + 1) % p;
List[index] = num[i];
Index[index] = true;
num[i] = index;
}
for(int i = 0; i < n; ++i)
{
if(i) printf(" ");
printf("%d", num[i]);
}
return 0;
}
字符串關鍵字的散列映射 (25分)
給定一系列由大寫英文字母組成的字符串關鍵字和素數PPP,用移位法定義的散列函數H(Key)H(Key)H(Key)將關鍵字KeyKeyKey中的最後3個字符映射爲整數,每個字符佔5位;再用除留餘數法將整數映射到長度爲PPP的散列表中。例如將字符串AZDEG
插入長度爲1009的散列表中,我們首先將26個大寫英文字母順序映射到整數0~25;再通過移位將其映射爲3×322+4×32+6=32063\times
32^2 + 4 \times 32 + 6 = 32063×322+4×32+6=3206;然後根據表長得到,即是該字符串的散列映射位置。
發生衝突時請用平方探測法解決。
輸入格式:
輸入第一行首先給出兩個正整數NNN(≤500\le 500≤500)和PPP(≥2N\ge 2N≥2N的最小素數),分別爲待插入的關鍵字總數、以及散列表的長度。第二行給出NNN個字符串關鍵字,每個長度不超過8位,其間以空格分隔。
輸出格式:
在一行內輸出每個字符串關鍵字在散列表中的位置。數字間以空格分隔,但行末尾不得有多餘空格。
輸入樣例1:
4 11
HELLO ANNK ZOE LOLI
輸出樣例1:
3 10 4 0
輸入樣例2:
6 11
LLO ANNA NNK ZOJ INNK AAA
輸出樣例2:
3 0 10 9 6 1
解題思路:
按照題中例子所說,模擬即可。
注意平方探測法,索引值出現負數時的情況以及關鍵字相同的情況
提交代碼:
編譯器:g++
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 1009;
struct infi{
char str[9];
bool ishave;
}Hash[MAXN];
int location[MAXN];
int Move[3] = {0, 5, 10};
int main()
{
int n, p;
char str[8];
scanf("%d%d", &n, &p);
for(int i = 0; i < n; ++i)
{
scanf("%s", str);
int len = strlen(str);
int key = 0;
for(int j = 0; j < 3 && len - j - 1 >= 0; ++j) //映射到一個數字
{
key += (str[len - j - 1] - 'A') * (1 << Move[j]);
}
key = key % p; //除留餘數法
for(int j = 0; true; ++j) //平方探測法
{
int di = (j * j) % p; //係數
int tmpkey = (key + di) %p; //正方向探測
if(!Hash[tmpkey].ishave)
{
location[i] = tmpkey, Hash[tmpkey].ishave = true;
strcpy(Hash[tmpkey].str, str);
break;
}
else if(strcmp(Hash[tmpkey].str, str) == 0)
{
location[i] = tmpkey;
break;
}
tmpkey = (key - di) % p; //負方向探測
if(tmpkey < 0) tmpkey = (tmpkey + p) % p;
if(!Hash[tmpkey].ishave)
{
location[i] = tmpkey, Hash[tmpkey].ishave = true;
strcpy(Hash[tmpkey].str, str);
break;
}
else if(strcmp(Hash[tmpkey].str, str) == 0)
{
location[i] = tmpkey;
break;
}
}
}
for(int i = 0; i < n; ++i)
{
if(i) printf(" ");
printf("%d", location[i]);
}
return 0;
}
電話聊天狂人 (25分)
給定大量手機用戶通話記錄,找出其中通話次數最多的聊天狂人。
輸入格式:
輸入首先給出正整數NNN(≤105\le 10^5≤105),爲通話記錄條數。隨後NNN行,每行給出一條通話記錄。簡單起見,這裏只列出撥出方和接收方的11位數字構成的手機號碼,其中以空格分隔。
輸出格式:
在一行中給出聊天狂人的手機號碼及其通話次數,其間以空格分隔。如果這樣的人不唯一,則輸出狂人中最小的號碼及其通話次數,並且附加給出並列狂人的人數。
輸入樣例:
4
13005711862 13588625832
13505711862 13088625832
13588625832 18087925832
15005713862 13588625832
輸出樣例:
13588625832 3
解題思路:
用除留餘數法定義散列函數。
此處使用拉鍊法解決衝突,因此使用數組指針。
提交代碼:
編譯器:g++
#include <iostream>
using namespace std;
const int MAXN = 100003;
typedef struct infi node;
struct infi{
long long num;
int cnt;
node * next;
};
node *L[MAXN];
int Find(long long n);
int main()
{
int n;
int peo = 0, cnt = 0;
long long n1, n2, Num = 0;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%lld %lld", &n1, &n2);
int cnt1 = Find(n1);
int cnt2 = Find(n2); //散列操作
if(cnt1 < cnt2) //一下兩個if語句確定最大值
n1 = n2, cnt1 = cnt2;
if(cnt1 > cnt)
cnt = cnt1, Num = n1;
}
for(int i = 0; i < MAXN; ++i)
{
while(L[i])
{
if(L[i]->cnt == cnt)
{
peo++;
if(Num > L[i]->num)
Num = L[i]->num;
}
node *tmp = L[i];
L[i] = L[i]->next;
delete tmp;
}
}
if(peo <= 1) printf("%lld %d\n", Num, cnt);
else printf("%lld %d %d\n", Num, cnt, peo);
return 0;
}
int Find(long long n)
{
int index = n % MAXN, cnt = 0;
node *tmp = L[index];
if(tmp == NULL)
{
tmp = new node;
tmp->next = NULL;
tmp->cnt = 1;
tmp->num = n;
L[index] = tmp;
return 1;
}
while(tmp->next != NULL && tmp->num != n)
{
tmp = tmp->next;
}
if(tmp == NULL)
{
node *p = new node;
p->num = n;
p->next = NULL;
p->cnt = 1;
tmp->next = p;
cnt = 1;
}
else if(tmp->num == n)
{
tmp->cnt++;
cnt = tmp->cnt;
}
return cnt;
}
QQ帳戶的申請與登陸 (25分)
實現QQ新帳戶申請和老帳戶登陸的簡化版功能。最大挑戰是:據說現在的QQ號碼已經有10位數了。
輸入格式:
輸入首先給出一個正整數NNN(≤105\le 10^5≤105),隨後給出NNN行指令。每行指令的格式爲:“命令符(空格)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 [email protected]
N 1234567890 [email protected]
N 1234567890 [email protected]
L 1234567890 myQQ@qq
L 1234567890 [email protected]
輸出樣例:
ERROR: Not Exist
New: OK
ERROR: Exist
ERROR: Wrong PW
Login: OK
解題思路:
本題參照上題思路,
具體還要考慮題中的L操作,對應可能有三種結果
題中的N操作,對應有兩種結果。
提交代碼:
編譯器:g++
#include <iostream>
#include <string.h>
using namespace std;
const int MOD = 100005;
typedef struct infi info;
struct infi{
long long ID;
char pass[21];
info *next;
};
infi *count[MOD];
int cnt[MOD];
int main()
{
int n;
char com[3], pw[15];
long long num;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
scanf("%s %lld %s", com, &num, pw);
int index = num % MOD; //除留餘數法
if(com[0] == 'L')
{
info *tmp = count[index];
bool isexit = false;
bool islaw = true;
while(tmp && !isexit) //查找是否存在賬號
{
if(tmp->ID == num) //檢驗用戶名
{
isexit = true;
if(strcmp(tmp->pass, pw) != 0) //判斷密碼是否正確
islaw = false;
}
tmp = tmp->next;
}
if(!isexit) printf("ERROR: Not Exist\n"); //賬號不存在
else
{
if(islaw) printf("Login: OK\n"); //賬號密碼都正確
else printf("ERROR: Wrong PW\n"); //賬號正確,密碼錯誤
}
}
else
{
info *tmp = count[index];
bool isexit = false;
while(tmp && !isexit) //查找賬號
{
if(tmp->ID == num)
isexit = true;
tmp = tmp->next;
}
if(isexit) printf("ERROR: Exist\n");
else //新建賬號
{
info *p = new info;
p->ID = num;
strcpy(p->pass, pw);
p->next = NULL;
if(cnt[index])
{
tmp = count[index];
while(tmp->next)
tmp = tmp->next;
tmp->next = p;
}
else
count[index] = p;
cnt[index]++;
printf("New: OK\n");
}
}
}
return 0;
}
航空公司VIP客戶查詢 (25分)
不少航空公司都會提供優惠的會員服務,當某顧客飛行里程累積達到一定數量後,可以使用里程積分直接兌換獎勵機票或獎勵升艙等服務。現給定某航空公司全體會員的飛行記錄,要求實現根據身份證號碼快速查詢會員里程積分的功能。
輸入格式:
輸入首先給出兩個正整數NNN(≤105\le
10^5≤105)和KKK(≤500\le
500≤500)。其中KKK是最低里程,即爲照顧乘坐短程航班的會員,航空公司還會將航程低於KKK公里的航班也按KKK公里累積。隨後NNN行,每行給出一條飛行記錄。飛行記錄的輸入格式爲:18位身份證號碼(空格)飛行里程
。其中身份證號碼由17位數字加最後一位校驗碼組成,校驗碼的取值範圍爲0~9和x共11個符號;飛行里程單位爲公里,是(0,
15 000]區間內的整數。然後給出一個正整數MMM(≤105\le
10^5≤105),隨後給出MMM行查詢人的身份證號碼。
輸出格式:
對每個查詢人,給出其當前的里程累積值。如果該人不是會員,則輸出No Info
。每個查詢結果佔一行。
輸入樣例:
4 500
330106199010080419 499
110108198403100012 15000
120104195510156021 800
330106199010080419 1
4
120104195510156021
110108198403100012
330106199010080419
33010619901008041x
輸出樣例:
800
15000
1000
No Info
解題思路:
此題中將身份證作爲關鍵字,將身份證編號轉化爲11進制的數字(X代表10)
然後用除留餘數法定義散列函數,用拉鍊法解決衝突。
提交代碼:
編譯器:g++
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int MAXN = 100009;
typedef struct infi info;
struct infi{
char ID[21];
int totKM;
info *next;
};
info peo[MAXN];
long long getIndex(char *str); //將身份證映射爲數字
int main()
{
int n, k, m;
int KM;
char ID[21];
scanf("%d %d", &n, &k);
for(int i = 0; i < n; ++i)
{
scanf("%s %d", ID, &KM);
if(KM < k) KM = k;
int index = (int) (getIndex(ID) % MAXN); //除留餘數法
if(peo[index].next == NULL) //拉鍊法
{
info *tmp = new info;
tmp->next = NULL;
strcpy(tmp->ID, ID);
tmp->totKM = KM;
peo[index].next = tmp;
}
else
{
info *tmp = peo[index].next;
while(strcmp(tmp->ID, ID) && tmp->next)
tmp = tmp->next;
if(strcmp(tmp->ID, ID) == 0)
tmp->totKM += KM;
else
{
info *p = new info;
p->next = NULL;
p->totKM = KM;
strcpy(p->ID, ID);
tmp->next = p;
}
}
}
scanf("%d", &m); //查找
for(int i = 0; i < m; ++i)
{
scanf("%s", ID);
int index = (int)(getIndex(ID) % MAXN);
info *tmp = peo[index].next;
while(tmp)
{
if(strcmp(tmp->ID, ID) == 0)
break;
tmp = tmp->next;
}
if(tmp) printf("%d\n", tmp->totKM);
else printf("No Info\n");
}
return 0;
}
long long getIndex(char *str)
{
long long num = 0;
for(int i = 0; i < 17; ++i)
num = num * 10 + (str[i] - '0');
if(str[17] != 'x') num += (str[17] - '0');
else num += 10;
return num;
}