Hash表(也称为哈希表、散列表)是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。它通过把键值(Key)映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数称为哈希函数,存放记录的数组称为哈希表。
基本概念
- 哈希函数:将任意长度的输入(通常称为键或关键字)通过某种算法转换成固定长度的输出(即哈希值)。理想情况下,不同的输入应产生不同的哈希值,但在实际应用中,哈希冲突(即不同的输入产生相同的哈希值)是不可避免的。
- 哈希表:用于存储键值对的数组。哈希表的大小通常是固定的,或者有一个上限。通过哈希函数计算出的哈希值用于确定键值对在哈希表中的位置(索引)。
- 哈希冲突:当两个或多个不同的键通过哈希函数映射到哈希表的同一个位置时,就发生了哈希冲突。解决哈希冲突的方法有多种,如链地址法(使用链表)、开放定址法(线性探测、二次探测等)、再哈希法等。
操作
- 插入:计算键的哈希值,并根据哈希表的大小和某种策略(如取模运算)确定该键的存储位置。如果该位置已有元素,则根据解决哈希冲突的方法处理。
- 搜索:与插入类似,首先计算键的哈希值,然后定位到哈希表中的相应位置。如果该位置上的元素与搜索的键相同,则搜索成功;如果发生冲突,则根据解决哈希冲突的策略进行查找。
- 删除:定位到键值对的位置,将其从哈希表中移除。在链地址法中,可能只是删除链表中的一个节点;在开放定址法中,可能需要将后续的元素向前移动。
优点
- 查找效率高:在理想情况下,哈希表的查找、插入和删除操作的时间复杂度都是O(1)。
- 存储空间利用率高:相比于顺序存储,哈希表可以根据需要动态扩展。
缺点
- 哈希冲突:虽然可以通过不同的方法解决哈希冲突,但冲突的存在仍然会影响哈希表的性能。
- 对哈希函数的依赖:哈希函数的设计直接影响到哈希表的性能。
- 不支持顺序遍历:由于哈希表是通过哈希值来确定存储位置的,因此不能直接进行顺序遍历。
c代码实现示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 10 // 哈希表的大小
// 定义链表节点
typedef struct Node {
char* key;
int value;
struct Node* next;
} Node;
// 定义哈希表
typedef struct HashTable {
Node** buckets;
} HashTable;
// 哈希函数(简单示例,实际使用时应更复杂)
unsigned int hash(char* key) {
unsigned long hash = 5381;
int c;
while ((c = *key++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash % TABLE_SIZE;
}
// 初始化哈希表
HashTable* create_hash_table() {
HashTable* table = (HashTable*)malloc(sizeof(HashTable));
table->buckets = (Node**)malloc(TABLE_SIZE * sizeof(Node*));
for (int i = 0; i < TABLE_SIZE; i++) {
table->buckets[i] = NULL;
}
return table;
}
// 插入键值对到哈希表
void insert(HashTable* table, char* key, int value) {
unsigned int index = hash(key);
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->key = strdup(key);
newNode->value = value;
newNode->next = table->buckets[index];
table->buckets[index] = newNode;
}
// 从哈希表中查找键对应的值
int search(HashTable* table, char* key) {
unsigned int index = hash(key);
Node* current = table->buckets[index];
while (current != NULL) {
if (strcmp(current->key, key) == 0) {
return current->value;
}
current = current->next;
}
return -1; // 未找到
}
// 销毁哈希表
void destroy_hash_table(HashTable* table) {
for (int i = 0; i < TABLE_SIZE; i++) {
Node* current = table->buckets[i];
while (current != NULL) {
Node* temp = current;
current = current->next;
free(temp->key);
free(temp);
}
}
free(table->buckets);
free(table);
}
int main() {
HashTable* table = create_hash_table();
insert(table, "apple", 100);
insert(table, "banana", 200);
insert(table, "cherry", 300);
printf("Value of 'apple': %d\n", search(table, "apple"));
printf("Value of 'banana': %d\n", search(table, "banana"));
printf("Value of 'grape' (not found): %d\n", search(table, "grape"));
destroy_hash_table(table);
return 0;
}