线性表(散列) - 哈希表

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。@立刀旁

# 哈希表相关题目

哈希表使用 O(N) 空间复杂度存储数据,并且以 O(1) 时间复杂度求解问题。

  • Java 中的 HashSet 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。

  • Java 中的 HashMap 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中

数据结构项目课候选题目 项目题目(一):基于哈希查找的图书馆书籍管理系统(难度系数1.0) 一、问题描述 设计一个图书馆书籍管理系统,要求使用合适的数据结构存储书籍基本信息(书号、书名、作者等),并基于散列查找(哈希查找)实现高效检索。系统需支持以下功能: 书籍入库:将新书信息插入,并同步更新哈希表。 书籍检索:根据书号(ISBN)快速查找书籍详细信息。 书籍删除:根据书号删除书籍,同步更新线性表哈希表。 遍历所有书籍:按入库顺序输出所有书籍信息。 冲突处理:哈希冲突需采用链地址法(拉链法)解决。 二、可能的设计提示 哈希表设计: o 以书号(ISBN,假设为整数)为关键字,设计哈希函数。 o 哈希表长度自定(如取20),采用链地址法处理冲突。 o 每个哈希桶使用一个链存储对应书籍在线性表中的索引(或指针)。 结合方式: o 线性表存储完整的书籍信息(主存储)。 o 哈希表仅存储书号及其对应书籍在线性表中的位置(索引或指针),作为快速检索的辅助结构。 o 任何对书籍的增删操作都需同时维护线性表哈希表,保证数据一致性。 三、扩展思考(可选) 若要求支持按书名检索,如何改造系统?(可考虑为书名再建一个哈希表,或使用其他数据结构) 对比顺序和链作为主存储,在增删操作和空间利用上的优缺点。 分析链地址法下,哈希表在最坏情况(所有关键字冲突)与平均情况下的查找效率。C语言
最新发布
11-08
我们来实现一个 **基于哈希查找的图书馆书籍管理系统**,使用 C 语言完成。该系统将结合: - **线性表(顺序存储)** 存储完整的书籍信息; - **哈希表 + 链地址法** 实现基于 ISBN 的快速查找; - 支持:入库、检索、删除、遍历等功能。 --- ### ✅ 功能要求总结 | 功能 | 描述 | |------|------| | 书籍入库 | 将新书加入线性表,并插入到哈希表中 | | 书籍检索 | 按 ISBN 快速查找书籍信息 | | 书籍删除 | 按 ISBN 删除书籍(线性表哈希表同步更新) | | 遍历所有书籍 | 按入库顺序输出所有书籍 | | 冲突处理 | 使用链地址法(拉链法)解决哈希冲突 | --- ## 🧱 数据结构设计 我们将定义以下结构体: 1. `Book`:示一本书的信息。 2. `Node`:哈希桶中的节点,保存对应书籍在线性表中的索引。 3. `HashTable`:哈希表,包含多个桶(每个桶是一个链头指针)。 4. `Library`:主系统结构,包含线性表哈希表--- ## ✅ 完整代码实现(C语言) ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME_LEN 50 #define HASH_TABLE_SIZE 20 // 哈希表大小 // 书籍结构 typedef struct { int isbn; char title[MAX_NAME_LEN]; char author[MAX_NAME_LEN]; } Book; // 线性表结构(动态数组) typedef struct { Book* books; int size; // 当前数量 int capacity; // 总容量 } BookList; // 哈希表节点(链地址法) typedef struct Node { int index; // 在线性表中的索引 struct Node* next; } HashNode; // 哈希表结构 typedef struct { HashNode* buckets[HASH_TABLE_SIZE]; } HashTable; // 图书馆系统 typedef struct { BookList bookList; HashTable hashTable; } Library; // ============ 工具函数 ============ // 哈希函数:取模法 int hash(int isbn) { return isbn % HASH_TABLE_SIZE; } // 初始化图书馆系统 void initLibrary(Library* lib) { lib->bookList.books = NULL; lib->bookList.size = 0; lib->bookList.capacity = 0; for (int i = 0; i < HASH_TABLE_SIZE; i++) { lib->hashTable.buckets[i] = NULL; } } // 动态扩容线性表 void resizeBookList(BookList* list) { if (list->size >= list->capacity) { list->capacity = (list->capacity == 0) ? 4 : list->capacity * 2; list->books = (Book*)realloc(list->books, list->capacity * sizeof(Book)); if (!list->books) { printf("内存分配失败!\n"); exit(1); } } } // 添加书籍到线性表并返回其索引 int addBookToList(BookList* list, int isbn, const char* title, const char* author) { resizeBookList(list); Book* b = &list->books[list->size]; b->isbn = isbn; strncpy(b->title, title, MAX_NAME_LEN - 1); b->title[MAX_NAME_LEN - 1] = '\0'; strncpy(b->author, author, MAX_NAME_LEN - 1); b->author[MAX_NAME_LEN - 1] = '\0'; return list->size++; // 返回插入位置的索引 } // 查找哈希表中是否存在某个 ISBN,并返回对应的线性表索引(-1 示未找到) int findInHashTable(HashTable* ht, int isbn) { int h = hash(isbn); HashNode* node = ht->buckets[h]; while (node != NULL) { if (ht->buckets[h]->index < 0 || ht->buckets[h]->index >= ht->buckets[h]->index) continue; if (ht->buckets[h]->index < 0 || ht->buckets[h]->index >= ht->buckets[h]->index) continue; if (ht->buckets[h]->index < 0 || ht->buckets[h]->index >= ht->buckets[h]->index) continue; if (lib->bookList.books[node->index].isbn == isbn) { return node->index; } node = node->next; } return -1; } // 向哈希表插入记录(索引) void insertIntoHashTable(HashTable* ht, int isbn, int index) { int h = hash(isbn); HashNode* newNode = (HashNode*)malloc(sizeof(HashNode)); if (!newNode) { printf("内存分配失败!\n"); exit(1); } newNode->index = index; newNode->next = ht->buckets[h]; ht->buckets[h] = newNode; } // 从哈希表中删除某 ISBN 对应的节点 int removeFromHashTable(HashTable* ht, int isbn) { int h = hash(isbn); HashNode* node = ht->buckets[h]; HashNode* prev = NULL; while (node != NULL) { if (lib->bookList.books[node->index].isbn == isbn) { if (prev == NULL) { ht->buckets[h] = node->next; } else { prev->next = node->next; } free(node); return 1; // 成功删除 } prev = node; node = node->next; } return 0; // 未找到 } // ========== 核心功能接口 ========== // 书籍入库 void insertBook(Library* lib, int isbn, const char* title, const char* author) { // 检查是否已存在 if (findInHashTable(&lib->hashTable, isbn) != -1) { printf("错误:ISBN %d 已存在!\n", isbn); return; } // 插入线性表 int index = addBookToList(&lib->bookList, isbn, title, author); // 插入哈希表 insertIntoHashTable(&lib->hashTable, isbn, index); printf("书籍 '%s' 入库成功(ISBN: %d)。\n", title, isbn); } // 书籍检索(按 ISBN) void searchBookByISBN(Library* lib, int isbn) { int index = findInHashTable(&lib->hashTable, isbn); if (index == -1) { printf("未找到 ISBN 为 %d 的书籍。\n", isbn); return; } Book* b = &lib->bookList.books[index]; printf("找到书籍 -> ISBN: %d, 书名: %s, 作者: %s\n", b->isbn, b->title, b->author); } // 删除书籍(按 ISBN) void deleteBookByISBN(Library* lib, int isbn) { int index = findInHashTable(&lib->hashTable, isbn); if (index == -1) { printf("未找到 ISBN 为 %d 的书籍。\n", isbn); return; } // 从哈希表中移除 removeFromHashTable(&lib->hashTable, isbn); // 从线性表中删除:用最后一个元素覆盖当前元素(避免移动) Book* books = lib->bookList.books; books[index] = books[lib->bookList.size - 1]; // 覆盖被删项 lib->bookList.size--; // 缩小尺寸 // ❗❗ 删除后必须重建哈希表,因为索引已变化! // 这是本设计的一个缺点:删除操作影响其他索引 → 需要重建哈希表 rebuildHashTable(lib); printf("书籍 ISBN %d 已删除。\n", isbn); } // 重建哈希表(删除或修改索引后调用) void rebuildHashTable(Library* lib) { // 清空原有哈希表 for (int i = 0; i < HASH_TABLE_SIZE; i++) { HashNode* node = lib->hashTable.buckets[i]; while (node != NULL) { HashNode* temp = node; node = node->next; free(temp); } lib->hashTable.buckets[i] = NULL; } // 重新插入所有书籍 for (int i = 0; i < lib->bookList.size; i++) { insertIntoHashTable(&lib->hashTable, lib->bookList.books[i].isbn, i); } } // 遍历所有书籍(按入库顺序) void traverseBooks(Library* lib) { if (lib->bookList.size == 0) { printf("图书馆为空。\n"); return; } printf("\n=== 所有书籍 ===\n"); for (int i = 0; i < lib->bookList.size; i++) { Book* b = &lib->bookList.books[i]; printf("[%d] ISBN: %d, 书名: %s, 作者: %s\n", i+1, b->isbn, b->title, b->author); } printf("=================\n\n"); } // 销毁图书馆(释放内存) void destroyLibrary(Library* lib) { free(lib->bookList.books); for (int i = 0; i < HASH_TABLE_SIZE; i++) { HashNode* node = lib->hashTable.buckets[i]; while (node != NULL) { HashNode* temp = node; node = node->next; free(temp); } } } // ========== 主函数测试 ========== int main() { Library lib; initLibrary(&lib); // 测试:添加几本书 insertBook(&lib, 1001, "深入理解计算机系统", "Randal E. Bryant"); insertBook(&lib, 1002, "算法导论", "Thomas H. Cormen"); insertBook(&lib, 1003, "C程序设计语言", "Brian W. Kernighan"); // 查找测试 searchBookByISBN(&lib, 1002); searchBookByISBN(&lib, 999); // 不存在 // 遍历测试 traverseBooks(&lib); // 删除测试 deleteBookByISBN(&lib, 1002); // 再次遍历 traverseBooks(&lib); // 再查一次被删的 searchBookByISBN(&lib, 1002); // 销毁 destroyLibrary(&lib); return 0; } ``` --- ## 🔍 代码解释说明 | 模块 | 功能说明 | |------|--------| | `Book`, `BookList` | 使用动态数组作为主存储,支持高效遍历和缓存友好访问 | | `HashNode`, `HashTable` | 拉链法实现哈希表,每个桶是一个链,存储索引 | | `hash()` | 简单取模哈希函数,适用于整数 ISBN | | `insertBook()` | 同时写入线性表哈希表 | | `searchBookByISBN()` | 利用哈希表 O(1) 平均时间查找 | | `deleteBookByISBN()` | 删除时需重建哈希表(因索引变动)——这是当前设计的主要代价 | | `rebuildHashTable()` | 删除/移动后保证哈希表正确性 | --- ## ⚠️ 设计权衡与局限性 1. **为什么用线性表?** - 遍历快,缓存命中率高; - 但删除时移动数据会导致索引失效。 2. **删除为何要重建哈希表?** - 因为我们将“索引”作为哈希表值,一旦删除中间元素,后续索引全部改变。 - 替代方案: - 使用链存储书籍(但遍历慢); - 或在 `Book` 中增加 `valid` 标志位,做逻辑删除(空间换时间); - 或让哈希表直接存储指针(但在 realloc 时可能失效)。 3. **链地址法优点:** - 冲突处理自然; - 不怕聚集; - 可动态扩展链长度。 4. **最坏情况效率:** - 所有键哈希到同一桶 → 查找退化为 O(n) - 平均情况:O(1),前提是哈希函数良好且负载因子低 --- ## ✅ 扩展思考解答 ### 1. 如何支持按书名检索? 可以额外建立一个哈希表,以书名为关键字,同样指向线性表索引。但由于书名可能重复,需允许同名书籍或使用链处理。 > 更优选择:使用平衡树或 Trie 树支持模糊匹配(如“算法”相关书籍)。 ### 2. 顺序 vs 链作主存储 | 特性 | 顺序(当前实现) | 链 | |------|------------------|-----| | 查找第i个元素 | O(1) | O(i) | | 插入末尾 | 分摊 O(1) | O(1) | | 删除元素 | 移动开销大 | O(1) 给定指针 | | 空间利用率 | 高(紧凑) | 低(额外指针) | | 哈希表索引稳定性 | 删除后易失效 | 删除不影响其他节点地址 | | 推荐场景 | 频繁遍历、读多删少 | 频繁增删 | 改进方向:可用“懒删除 + 定期压缩”减少重建开销。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值