目录
搜索2
散列法
散列法是一种搜索算法,它根据各元素的值来确定存储的位置,然后将位置保存在散列表中,从而实现数据的高速搜索。
散列表是一种数据结构,能够包含对关键数据集合高效地执行动态插入,搜索,删除等操作。虽然链表也能进行同样的操作,但搜索和删除的复杂度都高达O(n)。如果不发生冲突,散列法插入和搜索元素的算法复杂度仅为O(1)。
散列表由容纳n个元素的数组S和根据关键字决定数组下标的函数组成。输入的关键字,由函数决定关键字在数组中存储的位置。
1.简单实现
散列表的实现:
insert(key)
S[h(key)]=key
search(key)
return S[h(key)]
当输入的关键字为字符串或其他类型,要将其转换为相应的整数。h(k)是根据k的值求数组下标的函数,称为散列函数,h(k)的返回值称为散列值。为了保证散列值小于数组S的长度n,要进行取余运算。
例如:h(k)=k n 是一种简单的散列函数。(mod是取余运算符)
2.解决冲突
对于一个散列函数,会出现不同的key值对应相同的散列值的情况,即发生了“冲突”。
当遇到“冲突”时,我们要解决冲突,常见的解决冲突的方法有:开放地址法,链地址法等等。
在这里我们使用双散列结构中的开放地址法。
此函数拥有两个参数,k为关键字key的值,i为发生冲突后计算散列值的次数。要注意的是,每次移动个位置,必须保证S的长度n与互质,否则会出现无法生成坐标的情况。
所以我让n为质数,然后取一个小于n的值为。
如下图所示:
散列法的实现:
int h1(int key) {
return key % n;
}
int h2(int key) {
return 1 + key % (n - 1);
}
int h(int key, int i) {
return (h1(key) + i * h2(key)) % n;
}
int insert(int S[],int key) {
i = 0;
while (1) {
int j = h(key, i);
if (S[j] == nil) {//用nil来表示S[J]当前位置为空
S[j] = key;
return j;
}
else i = i + 1;
}
}
int search(int S[], int key) {
i = 0;
while (1) {
int j = h(key, i);
if (S[j] == key) {
return j;
}
else if (S[j] == nil || i >= n) {
return nil;
}
else i = i + 1;
}
}
示例
第一行输入命令数n,随后n行按顺序输入n个命令,输入的字符串靳由AGCT组成。
insert str:向数组中添加字符串str。
find str:当数组中包含str时,输出yes,不包含输出no。
输入示例:
6
insert AAA
insert AAC
find AAA
find CCC
insert CCC
find CCC
输出示例:
yes
no
yes
#include<stdio.h>
#include<string.h>
#define m 13
char S[100][100];
int h1(int key) {
return key % m;
}
int h2(int key) {
return 1 + key % (m - 1);
}
int h(int key, int i) {
return (h1(key) + i * h2(key)) % m;
}
int getChar(char ch) {
if (ch == 'A')return 1;
if (ch == 'G')return 2;
if (ch == 'C')return 3;
if (ch == 'T')return 4;
}
int getKey(char str[]) {
int sum = 0,p=1;
for (int i = 0; i < strlen(str); i++) {
sum += p*getChar(str[i]);
p *= 5;
}
return sum;
}
int find(char str[]) {
int key = getKey(str);
for (int i = 0;; i++) {
int H = h(key, i);
if (strlen(S[H]) == 0)return 0;
else if (strcmp(S[H], str) == 0)return 1;
}
}
int insert(char str[]) {
int key =getKey(str);
for (int i = 0;; i++) {
int H = h(key, i);
if (strlen(S[H]) == 0) {
strcpy(S[H],str);
return 0;
}
else if (strcmp(S[H],str) == 0)return 1;
}
return 0;
}
int main() {
char com[10], str[10];
int i, n;
for (i = 0; i < 100; i++) {
S[i][0] = '\0';
}
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%s", com);
scanf("%s", str);
if (com[0] == 'i') {
insert(str);
}
else if (find(str)) {
printf("yes");
}
else printf("no");
}
return 0;
}
合理的运用不同用途的散列函数可以减少算法的复杂度,进而提高程序的效率。
读《挑战程序设计竞赛》(侵删)第九天 2021.3.1
( 2021.7.8 第一次修改)