(1) 掌握散列算法(散列函数、散列存储、散列查找)的实现;
(2) 掌握常用的冲突解决方法。
(1) 选散列函数 H(key) = key % p,取散列表长 m 为 10000,p 取小于 m 的最大素数;
(2) 测试 α 对于散列算法效率的影响;
分别测试将随机生成的5000个、7500个以及 p 个不重复的随机数序列放入该表中,采用线性探测法作为解决冲突方法时,各自的冲突总次数和聚集总次数
(3) 测试不同冲突解决方法对于散列算法效率的影响:
分别测试随机生成的5000个不重复的随机数序列放入该表中时,采用线性探测法和二次探测法各自的冲突总次数和聚集总次数。
(4) 自行设计实验输出,应使结果尽可能清晰地被展示。
#include
#include
#include
#include
#define HASHSIZE 10000 // 散列表长度
#define NULLKEY 0 // 空值标记
int conflictCount; // 冲突次数
int gatherCount; // 聚集次数
typedef struct {
int key;
int gather;
} ElemType;
// 散列表
typedef struct {
ElemType *elem;
int count;
} HashTable;
int InitHashTable(HashTable *table); // 初始化散列表
int Hash(int key); // 除留余数法
void CreateRandomTable(int arr[], int randMinNum, int randLength); // 生成哈希表数据,用数组存放
void knuthShuffle(int arr[], int length); // 洗牌算法
void swapInt(int *card_1, int *card_2); // 交换函数
void InsertHashByLD(HashTable *table, int key); // 线性探测-插入关键字到散列表
void InsertHashBySD(HashTable *table, int key); // 二次探测-插入关键字到散列表
void HashTableDestroy(HashTable *table); // 销毁哈希表
/**
* 哈希表/散列表的查找 实验二
*/
int main() {
// 业务逻辑
printf("~~~~~~~~~~ 实现散列算法并对不同α和不同冲突解决方法进行对比 ~~~~~~~~~~\n");
printf("\t\t====================================\n");
printf("\t\t 1 线性探测-自定义生成随机数序列\n");
printf("\t\t 2 线性探测-系统随机生成随机数序列\n");
printf("\t\t 3 二次探测-自定义生成随机数序列\n");
printf("\t\t 4 二次探测-系统随机生成随机数序列\n");
printf("\t\t 5 退出\n");
printf("\t\t 6 清屏\n");
printf("\t\t====================================\n");
int change = 1;
while (1) {
// 创建随机数种子
srand(time(NULL));
// 创建哈希表
HashTable table;
// 初始化哈希表
if (InitHashTable(&table) == -1) {
printf("申请地址失败");
return 0;
}
// 生成扑克+洗牌算法(可指定随机数范围,且不会重复)
int randMinNum = 1;
int randMaxNum = 500000;
int arr[randMaxNum - randMinNum];
CreateRandomTable(arr, randMinNum, (randMaxNum - randMinNum));
printf("请选择:");
scanf("%d", &change);
if (change == 1) {
// 从洗好的牌中连续抽前arrLength张出来
int arrLength = 5000;
printf("线性探测-请输入自定义随机数个数:");
scanf("%d", &arrLength);
for (int i = 0; i < arrLength; i++) {
InsertHashByLD(&table, arr[i]);
}
} else if (change == 2) {
// 从洗好的牌中连续抽前随机张出来
int arrLength = randMinNum + (rand() % HASHSIZE + 1);
for (int i = randMinNum; i <= arrLength; i++) {
InsertHashByLD(&table, arr[i]);
}
} else if (change == 3) {
// 从洗好的牌中连续抽前arrLength张出来
int arrLength = 5000;
printf("二次探测-请输入自定义随机数个数:");
scanf("%d", &arrLength);
for (int i = randMinNum; i <= (randMinNum + arrLength); i++) {
InsertHashBySD(&table, arr[i]);
}
} else if (change == 4) {
// 从洗好的牌中连续抽前随机张出来
int arrLength = randMinNum + (rand() % HASHSIZE + 1);
for (int i = randMinNum; i <= arrLength; i++) {
InsertHashBySD(&table, arr[i]);
}
} else if (change == 5) {
break;
} else if (change == 6) {
system("cls"); // 清屏
printf("~~~~~~~~~~ 实现散列算法并对不同α和不同冲突解决方法进行对比 ~~~~~~~~~~\n");
printf("\t\t====================================\n");
printf("\t\t 1 线性探测-自定义生成随机数序列\n");
printf("\t\t 2 线性探测-系统随机生成随机数序列\n");
printf("\t\t 3 二次探测-自定义生成随机数序列\n");
printf("\t\t 4 二次探测-系统随机生成随机数序列\n");
printf("\t\t 5 退出\n");
printf("\t\t 6 清屏\n");
printf("\t\t====================================\n");
} else {
printf("你的输入有误!!!\n");
}
if (change == 1 || change == 2 || change == 3 || change == 4) {
printf("冲突次数 %d\n", conflictCount);
printf("聚集次数 %d\n", gatherCount);
printf("\n");
}
conflictCount = 0;
gatherCount = 0;
// 销毁哈希表
HashTableDestroy(&table);
}
return 0;
}
// 初始化
int InitHashTable(HashTable *table) {
if (!table) {
return -1;
}
table->count = HASHSIZE; // 表长
table->elem = (ElemType *) malloc(sizeof(ElemType) * HASHSIZE); // 表空间
if (!table->elem) {
return -1; // 如果没有申请到地址,退出
}
for (int i = 0; i < HASHSIZE; i++) {
table->elem[i].key = NULLKEY; // 所有单元全部初始化为空
}
return 0; // 初始化成功
}
// 使用除留余数法创建哈希表
int Hash(int key) {
// 求出最大素数
for (int i = HASHSIZE; i > 0; i--) {
int j = 2;
for (; j <= i; j++) {
if (i % j == 0) {
break;
}
}
if (i == j) {
return key % i;
}
}
return -999; // 抛出一个错误
}
void CreateRandomTable(int arr[], int randMinNum, int randLength) {
if (randMinNum == 0) {
printf("生成的随机数不能包含哈希表空值标记 ( 0 ) \n");
return;
}
for (int i = randMinNum; i <= randLength; i++) {
arr[i] = i;
}
knuthShuffle(arr, randLength);
}
void knuthShuffle(int arr[], int length) {
for (int i = length - 1; i >= 1; i--) {
swapInt(&arr[i], &arr[rand() % (i + 1)]);
}
}
void swapInt(int *card_1, int *card_2) {
int tCard;
tCard = *card_1;
*card_1 = *card_2;
*card_2 = tCard;
}
// 线性探测法创建哈希表(这里保证散列表有足够的空间)
void InsertHashByLD(HashTable *table, int key) {
int hashIndex = Hash(key); // 除留取余的方式给新插入的值分配散列位置
while (table->elem[hashIndex].key != NULLKEY) { // 在插入的时候如果出现不等于空的位置,则说明遇到冲突
if (table->elem[hashIndex].gather == -1) {
gatherCount++; // 聚集
} else {
table->elem[hashIndex].gather = -1;
conflictCount++; // 冲突
}
hashIndex = (hashIndex + 1) % HASHSIZE; // 线性探测
}
table->elem[hashIndex].key = key; // 放入哈希表
}
// 二次探测法创建哈希表(这里保证散列表有足够的空间)
void InsertHashBySD(HashTable *table, int key) {
int count = 0;
int hashIndex = Hash(key); // 除留取余的方式给新插入的值分配散列位置
int pos = hashIndex;
while (table->elem[hashIndex].key != NULLKEY) { // 在插入的时候如果出现不等于空的位置,则说明遇到冲突
count++;
if (table->elem[hashIndex].gather == -1) {
gatherCount++; // 聚集
} else {
table->elem[hashIndex].gather = -1;
conflictCount++; // 冲突
}
hashIndex = (pos + count * count) % (HASHSIZE / 2); // 二次探测
}
table->elem[hashIndex].key = key; // 放入哈希表
}
void HashTableDestroy(HashTable *table) {
if (table->elem != NULL) {
free(table->elem);
table->elem = NULL;
table->count = 0;
}
}