【发布时间】:2021-04-25 06:30:45
【问题描述】:
我一直在从头开始为动态大小的哈希表编写 C 实现。我犯了一个严重错误,因为我的散列函数是基于散列表的容量的。由于容量随时间而变化,因此这是行不通的。为动态分配的哈希表开发哈希函数有哪些建议?
此外,我正在使用二次探测,因此我的大小调整基于此。例如,如果我的哈希表容量为 8,并且一个新键最初哈希到索引 0,我计算的新索引将是 1 (0 + 1^2)、5 (1 + 2^2)、14 (5 + 3 ^2) 等使用二次探测,我会停在 14,因为它大于 8。所以,我会创建一个容量为 15 的新哈希表。我想保留这个实现,但如果有更好的方法,我会打开改变它。无论如何,我仍在寻找如何为动态数组而不是静态数组开发哈希函数。
编辑: 我的意思是因为我的散列函数是基于散列表的容量,当我去检索表大小改变后的元素时,它不起作用。例如,在我的主程序中,我用“A”键删除元素,然后再次打印出表格,但 A 仍然存在。这是因为我使用哈希函数查找“A”存在的位置以将其删除,但是当我去删除“A”时哈希函数不同,因为当我插入“A”时,容量与我插入时不同试图删除它。所以,哈希函数没有把我带到正确的地方。
我读过一些关于当我调整哈希表的大小时,我只需要用新哈希表的大小重新哈希当前在哈希表中的所有元素。我只是想知道除此之外是否还有其他方法。
状态.h
#ifndef STATUS_H
#define STATUS_H
typedef enum status { FAILURE, SUCCESS } Status;
typedef enum boolean { FALSE, TRUE } Boolean;
#endif
HashTableElement.h
#ifndef KEY_AND_DATA_H
#define KEY_AND_DATA_H
#include "status.h"
typedef void* HASH_TABLE_ELEMENT;
/*Precondition: none
Postcondition: returns a handle to a new hash table element. Else returns NULL */
HASH_TABLE_ELEMENT hash_table_element_create(char* key, int data);
/*Precondition: hHash_table_element is a handle to a valid hash table element, data is the
new data value.
Postcondition: the data inside the hash table has been updated. */
void hash_table_element_update(HASH_TABLE_ELEMENT hHash_table_element, int data);
/*Precondition: hHash_table_element is a handle to a valid hash table element.
Postcondition: returns the data value. */
int hash_table_element_get_data(HASH_TABLE_ELEMENT hHash_table_element);
/*Precondition: hHash_table_element is a handle to a valid hash table element.
Postcondition: returns the key */
const char* hash_table_element_get_key(HASH_TABLE_ELEMENT hHash_table_element);
/*Precondition: hHash_table_element1 and 2 are handles to valid hash table elements.
Postcondition: returns true or false if the keys match or not*/
Boolean hash_table_element_keys_match(HASH_TABLE_ELEMENT hHash_table_element1,
HASH_TABLE_ELEMENT hHash_table_element2);
Status hash_table_element_get_character_by_index(HASH_TABLE_ELEMENT hHash_table_element, int index, char* ch);
void hash_table_element_destroy(HASH_TABLE_ELEMENT* phHash_table_element);
#endif
哈希表.h
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include "status.h"
typedef void* HASH_TABLE;
/* Precondition: none
Postcondition: returns a handle to an empty hash table or NULL on Failure */
HASH_TABLE hash_table_init_default(unsigned initial_capacity);
/* Precondition: capacity is the capacity of the hash table.
key is the key to be hased.
Postcondition: returns an index in the hash table that comes from
hasing the key with the hash table capacity */
unsigned hash_table_hash(unsigned capacity, char* key);
/* Precondition: hHash_table is a handle to a valid hash_table
Postcondition: returns the capacity */
unsigned hash_table_get_capacity(HASH_TABLE hHash_table);
/* Precondition: hHash_table is a handle to a valid hash table. Key and data
are the info to be put into the hash_table
Postcondition: a new element has been created and inserted in the hash table
Returns FAILURE for any memory allocation failure */
Status hash_table_insert(HASH_TABLE hHash_table, char* key, int data);
/* Precondition: hHash_table is a handle to a valid hash table object. Key is the
key to search for.
Postcondition: if the key exists, stores it in data and returns SUCCESS. Else,
returns FAILURE and stores a 0 in data */
Status hash_table_get_data_by_key(HASH_TABLE hHash_table, char* key, int* data);
/* Precondition: hHash_table is a handle to a hash table. key is the key to be looked for.
Postcondition: if the key exists, stores the index in indexOfKey and returns true. If it
doesn't, returns false and stors a 0 in indexOfKey */
Boolean hash_table_get_key_index(HASH_TABLE hHash_table, char* key, unsigned* indexOfKey);
/* Precondition: hHash_table is a handle to a hash table. Index is the index to search.
Data stores the data at the index.
Postcondition: returns SUCCESS and stores the data value at that index in data. If the index
caused overflow, or the index was NULL, returns FAILIURE and data is set to 0 */
Status hash_table_get_data_by_index(HASH_TABLE hHash_table, int index, int* data);
/* Precondition: hHash_table is a handle to a hash table. Index is the index to search.
Data stores the data at the index.
Postcondition: returns SUCCESS and stores the key at that index in key. If the index
caused overflow, or the index was NULL, returns FAILIURE and key is set as the empty string */
Status hash_table_get_key_by_index(HASH_TABLE hHash_table, int index, char* key);
/* Precondition: hHash_table is a handle to a valid hash table object. Key is the
key to be searched for
Postcondition: if the element corresponding to the key exists, it is removed and
SUCCESS is returned. Else, it FAILURE is returned */
Status hash_table_remove_element(HASH_TABLE hHash_table, char* key);
/* Precondition: phHash_table is a pointer to a handle to a hash table
Postcondion: all memory associated with the hash table has been freed.
and the hash table handle is set to NULL */
void hash_table_destroy(HASH_TABLE* phHash_table);
void debug(HASH_TABLE hHash_table);
#endif
HashTableElement.c
#include "HashTableElement.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hash_table_element {
char* key;
int data;
unsigned capacity; // capacity of hash table during creation
} Hash_table_element;
HASH_TABLE_ELEMENT hash_table_element_create(char* key, int data) {
Hash_table_element* pHash_table_element = (Hash_table_element*)malloc(sizeof(Hash_table_element));
if (pHash_table_element != NULL) {
pHash_table_element->key = (char*)malloc(sizeof(char) * (strlen(key) + 1));
if (pHash_table_element->key == NULL) {
free(pHash_table_element);
return NULL;
}
for (unsigned i = 0; i < strlen(key); i++)
pHash_table_element->key[i] = key[i];
pHash_table_element->key[strlen(key)] = '\0';
pHash_table_element->data = data;
}
return (HASH_TABLE_ELEMENT)pHash_table_element;
}
void hash_table_element_update(HASH_TABLE_ELEMENT hHash_table_element, int data) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
pHash_table_element->data = data;
}
int hash_table_element_get_data(HASH_TABLE_ELEMENT hHash_table_element) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
return pHash_table_element->data;
}
const char* hash_table_element_get_key(HASH_TABLE_ELEMENT hHash_table_element) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
return (const char*)pHash_table_element->key;
}
Boolean hash_table_element_keys_match(HASH_TABLE_ELEMENT hHash_table_element1,
HASH_TABLE_ELEMENT hHash_table_element2) {
Hash_table_element* pHash_table_element1 = (Hash_table_element*)hHash_table_element1;
Hash_table_element* pHash_table_element2 = (Hash_table_element*)hHash_table_element2;
if (!strcmp(pHash_table_element1->key, pHash_table_element2->key))
return TRUE;
return FALSE;
}
Status hash_table_element_get_character_by_index(HASH_TABLE_ELEMENT hHash_table_element, int index, char* ch) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
if (index > strlen(pHash_table_element->key)) {
*ch = '\0';
return FAILURE;
}
*ch = pHash_table_element->key[index];
return SUCCESS;
}
void hash_table_element_destroy(HASH_TABLE_ELEMENT* phHash_table_element) {
if (*phHash_table_element != NULL) {
Hash_table_element* pHash_table_element = (Hash_table_element*)*phHash_table_element;
free(pHash_table_element->key);
free(pHash_table_element);
*phHash_table_element = NULL;
}
}
哈希表.c
#include "HashTable.h"
#include "HashTableElement.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hash_table {
HASH_TABLE_ELEMENT* table;
unsigned capacity;
} Hash_table;
HASH_TABLE hash_table_init_default(unsigned initial_capacity) {
Hash_table* pHash_table = (Hash_table*)malloc(sizeof(Hash_table));
if (pHash_table != NULL) {
pHash_table->table = (HASH_TABLE_ELEMENT*)malloc(sizeof(HASH_TABLE_ELEMENT) * initial_capacity);
if (pHash_table->table == NULL) {
free(pHash_table);
return NULL;
}
for (unsigned i = 0; i < initial_capacity; i++) {
pHash_table->table[i] = NULL;
}
pHash_table->capacity = initial_capacity;
}
return (HASH_TABLE)pHash_table;
}
unsigned hash_table_hash(unsigned capacity, char* key) {
unsigned sum = 0;
for (unsigned i = 0; i < strlen(key); i++)
sum += key[i];
return sum % capacity;
}
unsigned hash_table_get_capacity(HASH_TABLE hHash_table) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
return pHash_table->capacity;
}
Status hash_table_insert(HASH_TABLE hHash_table, char* key, int data) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
unsigned index = hash_table_hash(pHash_table->capacity, key);
unsigned quadraticNum = 1;
Boolean overflow = (Boolean)(index >= pHash_table->capacity);
while (!overflow && pHash_table->table[index] != NULL) {
if (!strcmp(hash_table_element_get_key(pHash_table->table[index]), key)) {
hash_table_element_update(pHash_table->table[index], data);
return SUCCESS;
}
else {
index += quadraticNum * quadraticNum;
quadraticNum++;
if (index >= pHash_table->capacity) {
overflow = TRUE;
}
}
}
if (overflow) {
unsigned newCapacity = index + 1;
HASH_TABLE_ELEMENT* newTable = (HASH_TABLE_ELEMENT*)malloc(sizeof(HASH_TABLE_ELEMENT) * newCapacity);
if (newTable == NULL)
return FAILURE;
for (unsigned i = 0; i < pHash_table->capacity; i++) {
if (pHash_table->table[i] == NULL)
newTable[i] = NULL;
else {
newTable[i] =
hash_table_element_create(hash_table_element_get_key(pHash_table->table[i]),
hash_table_element_get_data(pHash_table->table[i]));
if (newTable[i] == NULL) {
for (int j = i - 1; j >= 0; j--)
hash_table_element_destroy(&(newTable[j]));
free(newTable);
return FAILURE;
}
}
}
for (unsigned i = pHash_table->capacity; i < newCapacity - 1; i++)
newTable[i] = NULL;
newTable[newCapacity - 1] = hash_table_element_create(key, data, pHash_table->capacity);
if (newTable[newCapacity - 1] == NULL) {
for (int i = newCapacity - 2; i >= 0; i--)
hash_table_element_destroy(&(newTable[i]));
free(newTable);
return FAILURE;
}
for (unsigned i = 0; i < pHash_table->capacity; i++)
hash_table_element_destroy(&(pHash_table->table[i]));
free(pHash_table->table);
pHash_table->table = newTable;
pHash_table->capacity = newCapacity;
return SUCCESS;
}
else {
pHash_table->table[index] = hash_table_element_create(key, data, pHash_table->capacity);
if (pHash_table->table[index] == NULL)
return FAILURE;
return SUCCESS;
}
}
Boolean hash_table_get_key_index(HASH_TABLE hHash_table, char* key, unsigned* indexOfKey) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
unsigned index = hash_table_hash(pHash_table->capacity, key);
unsigned quadraticNum = 1;
while (index < pHash_table->capacity) {
if (pHash_table->table[index] != NULL) {
if (!strcmp(key, hash_table_element_get_key(pHash_table->table[index]))) {
*indexOfKey = index;
return TRUE;
}
}
index += quadraticNum * quadraticNum;
quadraticNum++;
}
*indexOfKey = 0;
return FALSE;
}
Status hash_table_get_data_by_key(HASH_TABLE hHash_table, char* key, int* data) {
unsigned indexOfKey = 0;
if (hash_table_get_key_index(hHash_table, key, &indexOfKey)) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
*data = hash_table_element_get_data(pHash_table->table[indexOfKey]);
return SUCCESS;
}
*data = 0;
return FAILURE;
}
Status hash_table_get_data_by_index(HASH_TABLE hHash_table, int index, int* data) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
if (index >= pHash_table->capacity || pHash_table->table[index] == NULL) {
*data = 0;
return FAILURE;
}
*data = hash_table_element_get_data(pHash_table->table[index]);
return SUCCESS;
}
Status hash_table_get_key_by_index(HASH_TABLE hHash_table, int index, char* key) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
if (index >= pHash_table->capacity || pHash_table->table[index] == NULL) {
key[0] = '\0';
return FAILURE;
}
char ch;
for (unsigned i = 0; i < strlen(hash_table_element_get_key(pHash_table->table[index])); i++) {
hash_table_element_get_character_by_index(pHash_table->table[index], i, &key[i]);
}
key[strlen(hash_table_element_get_key(pHash_table->table[index]))] = '\0';
return SUCCESS;
}
Status hash_table_remove_element(HASH_TABLE hHash_table, char* key) {
unsigned indexOfKey = 0;
if (hash_table_get_key_index(hHash_table, key, &indexOfKey)) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
hash_table_element_destroy(&(pHash_table->table[indexOfKey]));
return SUCCESS;
}
return FAILURE;
}
void hash_table_destroy(HASH_TABLE* phHash_table) {
Hash_table* pHash_table = (Hash_table*)*phHash_table;
for (unsigned i = 0; i < pHash_table->capacity; i++)
hash_table_element_destroy(&(pHash_table->table[i]));
free(pHash_table->table);
free(pHash_table);
*phHash_table = NULL;
}
void debug(HASH_TABLE hHash_table) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
int data;
char key[100];
char DNE[4] = "DNE";
for (unsigned i = 0; i < pHash_table->capacity; i++) {
printf("Index: %-10d", i);
Status keyStatus = hash_table_get_key_by_index(hHash_table, i, key);
Status dataStatus = hash_table_get_data_by_index(hHash_table, i, &data);
if (keyStatus == FAILURE && dataStatus == FAILURE) {
printf("Key: %-10sData: %-10s\n", DNE, DNE);
}
else {
printf("Key: %-10sData: %-10d\n", key, data);
}
}
}
main.c
#include <stdio.h>
#include "HashTable.h"
#include <string.h>
#include <vld.h>
int main(int argc, char** argv) {
HASH_TABLE hHash_table = hash_table_init_default(5);
char key[3] = "A";
unsigned num = 1;
for (unsigned i = 0; i < 26; i++) {
hash_table_insert(hHash_table, key, num);
key[0] = key[0] + 1;
num++;
}
debug(hHash_table);
printf("\n\n\n");
hash_table_remove_element(hHash_table, "A");
debug(hHash_table);
hash_table_destroy(&hHash_table);
return 0;
}
输出
Visual Leak Detector read settings from: C:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
Index: 0 Key: A Data: 1
Index: 1 Key: B Data: 2
Index: 2 Key: C Data: 3
Index: 3 Key: D Data: 4
Index: 4 Key: E Data: 5
Index: 5 Key: F Data: 6
Index: 6 Key: G Data: 7
Index: 7 Key: H Data: 8
Index: 8 Key: S Data: 19
Index: 9 Key: Q Data: 17
Index: 10 Key: J Data: 10
Index: 11 Key: K Data: 11
Index: 12 Key: L Data: 12
Index: 13 Key: M Data: 13
Index: 14 Key: N Data: 14
Index: 15 Key: I Data: 9
Index: 16 Key: O Data: 15
Index: 17 Key: P Data: 16
Index: 18 Key: V Data: 22
Index: 19 Key: W Data: 23
Index: 20 Key: X Data: 24
Index: 21 Key: Y Data: 25
Index: 22 Key: Z Data: 26
Index: 23 Key: T Data: 20
Index: 24 Key: R Data: 18
Index: 25 Key: DNE Data: DNE
Index: 26 Key: DNE Data: DNE
Index: 27 Key: DNE Data: DNE
Index: 28 Key: DNE Data: DNE
Index: 29 Key: DNE Data: DNE
Index: 30 Key: DNE Data: DNE
Index: 31 Key: DNE Data: DNE
Index: 32 Key: DNE Data: DNE
Index: 33 Key: DNE Data: DNE
Index: 34 Key: DNE Data: DNE
Index: 35 Key: DNE Data: DNE
Index: 36 Key: DNE Data: DNE
Index: 37 Key: DNE Data: DNE
Index: 38 Key: DNE Data: DNE
Index: 39 Key: DNE Data: DNE
Index: 40 Key: U Data: 21
Index: 0 Key: A Data: 1
Index: 1 Key: B Data: 2
Index: 2 Key: C Data: 3
Index: 3 Key: D Data: 4
Index: 4 Key: E Data: 5
Index: 5 Key: F Data: 6
Index: 6 Key: G Data: 7
Index: 7 Key: H Data: 8
Index: 8 Key: S Data: 19
Index: 9 Key: Q Data: 17
Index: 10 Key: J Data: 10
Index: 11 Key: K Data: 11
Index: 12 Key: L Data: 12
Index: 13 Key: M Data: 13
Index: 14 Key: N Data: 14
Index: 15 Key: I Data: 9
Index: 16 Key: O Data: 15
Index: 17 Key: P Data: 16
Index: 18 Key: V Data: 22
Index: 19 Key: W Data: 23
Index: 20 Key: X Data: 24
Index: 21 Key: Y Data: 25
Index: 22 Key: Z Data: 26
Index: 23 Key: T Data: 20
Index: 24 Key: R Data: 18
Index: 25 Key: DNE Data: DNE
Index: 26 Key: DNE Data: DNE
Index: 27 Key: DNE Data: DNE
Index: 28 Key: DNE Data: DNE
Index: 29 Key: DNE Data: DNE
Index: 30 Key: DNE Data: DNE
Index: 31 Key: DNE Data: DNE
Index: 32 Key: DNE Data: DNE
Index: 33 Key: DNE Data: DNE
Index: 34 Key: DNE Data: DNE
Index: 35 Key: DNE Data: DNE
Index: 36 Key: DNE Data: DNE
Index: 37 Key: DNE Data: DNE
Index: 38 Key: DNE Data: DNE
Index: 39 Key: DNE Data: DNE
Index: 40 Key: U Data: 21
No memory leaks detected.
Visual Leak Detector is now exiting.
C:\UML\Computer Science\COMP.1020 Computing II\Interfaces\Hash Table ADT\No Duplicates\Hash Table ADT\Debug\Hash Table ADT.exe (process 24304) exited with code 0.
Press any key to close this window . . .
【问题讨论】:
-
请编辑您的问题并发布您编写的代码。我不确定我理解你的意思:我犯了一个严重错误,因为我的哈希函数是基于哈希表的容量。 通常,哈希函数只需要一个缓冲区指针和长度.它不关心有多少哈希桶,也不关心哈希桶的数量是否动态变化。
-
我编辑了我的 cmets 并添加了代码。我希望我已经说清楚了我在问什么,如果不让我知道的话。
-
你有
unsigned capacity;和你的return sum % capacity;来自你的哈希函数,所以不管表大小,只要你在扩展表大小时更新了capacity- 一切都会无缝地工作. -
是的,但现在它不起作用。例如,当我最初插入“A”时,容量为 5,而 A 在 ASCII 表中为 65,因此 65 % 5 = 索引 0。但是,当我删除 A 时,容量已更改为 41。41 % 5 为 1。因此,当我去删除 A 时,它开始查看索引 1 并进行二次探测,直到它超过表的末尾并且永远找不到 A。您将在我的 hash_table_remove_element 函数中看到它是如何发挥作用的,该函数调用 hash_table_get_key_index 来查找索引.正是这个索引查找功能找到了错误的索引。我唯一能想到的就是重新散列
-
如果您将我的 main.c 程序与输出进行比较,我预计会在第二轮输出中看到“A”被删除,但它仍然存在。
标签: c hashtable hash-function