重建哈希表
- 如何重建哈希表。还当插入关键字时,如果发生的冲突次数 达到哈希表长度的一半,表明当前哈希表已经发生了 “堆聚” 现象,为了减少这种现象,提高查找效率,我们需要重建哈希表。
重建哈希表的原理很简单,把原来数组里已有的关键字取出来临时存储,然后将原来数组扩大,再把关键字–插回去就可以了。
接下来,我们就来实现哈希表的重建函数recreate. 首先我们先把定义写好,recreate 没有返回值,参数只有一个HashTable 类型的指针h. - 我们先来完成第一步,将原来数组里的元素都取出来,存到临时空间里。 首先我们定义一个char 类型的二维指针变量 temp_elem, 然后我们给temp_elem 申请h->size和个 char * 类型的存储空间。这里我们分成两行来写,借助malloc 来分配内存。
- 接下来我们用for 循环,用变量 i 从 0 循环到不小于 h->size 时退出。如果原来哈希数组当前位置不为空,则给temp_elem[i] 分配 strlen(h->elem[i]) + 1大小的内存空间。然后将原哈希表里的关键字取出,存放在临时数组temp_elem[i] 里。
for 循环 记得写上花括号,字符串拷贝用strcpy 函数。 - 如果哈希表当前位置没有元素,则将临时哈希表的当前第i 个位置也置为空。这里我们直接用else 语句中实现它的逻辑就好了。
- 接下来我们先释放哈希表 h 原来 elem 的存储空间。首先写一个for 循环,用int 类型的变量 i 从 0 循环到不小于 h->size 时退出。for 循环内完成对每个不为空的字符串 elem[i] 进行内存空间回收。
在for 循环后面再完成对二维指针变量elem的空间回收,指针回收用free. - 接下来 我们要扩大哈希表了, 在这之前我们需要定义一个 int 类型的指针变量copy_size, 存储原来的哈希表h 的长度 size , 因为后面我们还会用到size . 然后,我们把size 扩大一倍,做为新的哈希表长度。最后,我们给新的elem 申请 size 个 char * 类型的存储空间。
- 哈希数组扩大后,我们要使用for 循环把哈希表h 上的每个字符串 elem[i] 置为空。
- 接下来我们把原来哈希表里的关键字-- 插入到新的哈希表里。这里我们先写好for 循环,用变量 i 从 0 循环到不小于 copy_size 时退出来完成循环。
注意此时不能用size ,因为size 现在存的是新的哈希表长度,copy_size 存的才是原来的哈希表长度。 - 那么现在我们在for 循环里, 把关键字插入到新的哈希表里。 因为不是把哈希表里的每一位都插入,而是只有存储了关键字的元素才需要插入,所以我们还先要判断当前位置上是否为空,如果不为空则调用 insert 函数将该位置上的关键字插入到哈希表里。
- 现在只剩下最后一步了,在recreate 最后将temp_elem 指向的空间删除,避免内存泄露。
首先写一个for 循环,用int 类型的变量 i 从 0 循环到小于 copy_size. for 循环内判断当前位置是否为空,不为空则把temp_elem[i] 的空间回收。在for 循环后面再完成对二维指针变量temp_elem 的空间回收。指针回收用free.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct HashTable {
char **elem;
int size;
} HashTable;
void init(HashTable *h);
int hash(HashTable *h, char value[]);
int search(HashTable *h, char value[], int *pos, int *times);
int insert(HashTable *h, char value[]);
void recreate(HashTable *h);
void init(HashTable *h) {
h->size = 2000;
h->elem = (char **)malloc(sizeof(char *) * h->size);
for (int i = 0; i < h->size; i++) {
h->elem[i] = NULL;
}
}
int hash(HashTable *h, char value[]) {
int code = 0;
for (size_t i = 0; i < strlen(value); i++) {
code = (code * 256 + value[i] + 128) % h->size;
}
return code;
}
int search(HashTable *h, char value[], int *pos, int *times) {
*pos = hash(h, value);
*times = 0;
while (h->elem[*pos] != NULL && strcmp(h->elem[*pos], value) != 0) {
(*times)++;
if (*times < h->size) {
*pos = (*pos + 1) % h->size;
} else {
return 0;
}
}
if (h->elem[*pos] != NULL && strcmp(h->elem[*pos], value) == 0) {
return 1;
} else {
return 0;
}
}
int insert(HashTable *h, char value[]) {
int pos, times;
if (search(h, value, &pos, ×)) {
return 2;
} else if (times < h->size / 2) {
h->elem[pos] = (char *)malloc(strlen(value) + 1);
strcpy(h->elem[pos], value);
return 1;
} else {
recreate(h);
insert(h, value);
return 0;
}
}
void recreate(HashTable *h) {
char **temp_elem;
temp_elem = (char **)malloc(sizeof(char *) * h->size);
for(int i = 0; i<h->size; i ++) {
if(h->elem[i] != NULL) {
temp_elem[i] = (char *)malloc(strlen(h->elem[i]) + 1);
strcpy(temp_elem[i], h->elem[i]);
} else {
temp_elem[i] = NULL;
}
}
for(int i = 0; i < h -> size; i++) {
if(h->elem[i] != NULL){
free(h->elem[i]);
}
}
free(h->elem);
int copy_size = h->size;
h->size = h->size * 2;
h->elem = (char **)malloc(sizeof(char *) * h->size);
for(int i =0; i < h->size; i++) {
h->elem[i] = NULL;
}
for(int i = 0; i < copy_size; i++) {
if(temp_elem[i] != NULL){
insert(h,temp_elem[i]);
}
}
for(int i = 0; i < copy_size; i++) {
if(temp_elem[i] != NULL) {
free(temp_elem[i]);
}
}
free(temp_elem);
}
void clear(HashTable *h) {
for (int i = 0; i < h->size; ++i) {
if (h->elem[i] != NULL) {
free(h->elem[i]);
}
}
free(h->elem);
free(h);
}
int main() {
HashTable *hashtable = (HashTable*)malloc(sizeof(HashTable));
init(hashtable);
char buffer[1000];
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", buffer);
int ans = insert(hashtable, buffer);
if (ans == 0) {
printf("recreate while insert!\n");
} else if (ans == 1) {
printf("insert success!\n");
} else if (ans == 2) {
printf("It already exists!\n");
}
}
int temp_pos, temp_times;
scanf("%s", buffer);
if (search(hashtable, buffer, &temp_pos, &temp_times)) {
printf("search success!\n");
} else {
printf("search failed!\n");
}
clear(hashtable);
return 0;
}