散列表(二):冲突处理的方法之链地址法的实现(哈希查找)

本文详细介绍了链地址法处理哈希冲突的原理,包括将关键码分配到同义词链中,以及如何利用链表结构实现哈希查找、插入和删除操作。通过实例展示了链地址法在存储和搜索效率上的优势,并提供了链地址法的C语言实现,包括构造、释放哈希表及增删查改操作。此外,还讨论了链地址法相对于开地址法在存储空间上的节省。
摘要由CSDN通过智能技术生成

首先需要澄清的一点是,这里讲的是hash table/hash map ,即数据项所存储的表要用数组来实现。


一、链地址法

这种基本思想:将所有哈希地址为i 的元素构成一个称为同义词链的链表,并将链表的头指针存在哈希表的第i个单元中,因而查找、入和删除主要在

同义词链中进行。 


该散列方法首先对关键码集合用某一个散列函数计算它们的存放位置。

若设散列表地址空间的所有位置是从0到m-1,则关键码集合中的所有关键码被划分为m个子集,具有相同地址的关键码归于同一子集。我们称同一子集

中的关键码互为同义词。每一个子集称为一个桶。


通常各个桶中的表项通过一个链表链接起来,称之为同义词子表。所有桶号相同的表项都链接在同一个同义词子表中,各链表的表结点

组成一个向量。


假设给出一组表项,它们的关键码为 Burke, Ekers, Broad, Blum, Attlee, Alton, Hecht, Ederly。采用的散列函数是:取其第一个字母在

字母表中的位置。 

hash (x) = ord (x) - ord (‘A’) 

这样,可得

hash (Burke) = 1hash (Ekers) = 4

hash (Broad) = 1hash (Blum) = 1

hash (Attlee) = 0hash (Hecht) = 7

hash (Alton) = 0hash (Ederly) = 4



1、通常,每个桶中的同义词子表都很短,设有n个关键码通过某一个散列函数,存放到散列表中的 m 个桶中。那么每一个桶中的同

义词子表的平均长度为 n / m。这样,以搜索平均长度为 n / m 的同义词子表代替了搜索长度为 n 的顺序表,搜索速度快得多(O(1))。

2、应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上,由于开地址法必须保持大量的空闲空间以确保搜索

效率,如二次探查法要求装填因子,(a = n / m)而表项所占空间又比指针大得多,所以使用链地址法反而比开地址法节省存

储空间。


下面给出链地址法的实现,包括构造哈希表,释放哈希表,在哈希表中根据key查找一项,根据key 插入一项,根据key 删除一项等。链表节点用双向

链表实现。


common.h:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef _COMMON_H_
#define _COMMON_H_

#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define ERR_EXIT(m) \
   do \
  { \
    perror(m); \
    exit(EXIT_FAILURE); \
  } \
   while ( 0)

#endif

hash_link.h:

 C++ Code 
  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
散列表(hash table)是一种常见的数据结构,它允许根据键(key)快速查找值(value)。散列表的基本思想是将键通过一个哈希函数(hash function)映射到一个数组下标,然后在该位置存储对应的值。 当不同的键被映射到同一个数组下标时,就会发生冲突(collision)。解决冲突方法有很多种,其中一种常见的方法地址(chaining)。 地址的基本思想是,在每个散列表的位置上,维护一个表。当一个键被映射到该位置时,就将它插入到表中。这样,当发生冲突时,就可以将不同的键存储在同一个位置上,并通过表来管理它们。 下面是使用链地址实现散列表查找的示例代码: ``` #include <stdio.h> #include <stdlib.h> #define HASHSIZE 5 typedef struct node { int key; int value; struct node *next; } Node; typedef struct hashtable { Node *table[HASHSIZE]; } HashTable; int hash(int key) { return key % HASHSIZE; } Node *search(HashTable *ht, int key) { int index = hash(key); Node *p = ht->table[index]; while (p != NULL && p->key != key) { p = p->next; } return p; } void insert(HashTable *ht, int key, int value) { int index = hash(key); Node *p = ht->table[index]; while (p != NULL && p->key != key) { p = p->next; } if (p == NULL) { Node *new_node = (Node *)malloc(sizeof(Node)); new_node->key = key; new_node->value = value; new_node->next = ht->table[index]; ht->table[index] = new_node; } else { p->value = value; } } void print_table(HashTable *ht) { for (int i = 0; i < HASHSIZE; i++) { printf("%d: ", i); Node *p = ht->table[i]; while (p != NULL) { printf("(%d, %d) ", p->key, p->value); p = p->next; } printf("\n"); } } int main() { HashTable ht; for (int i = 0; i < HASHSIZE; i++) { ht.table[i] = NULL; } insert(&ht, 1, 10); insert(&ht, 2, 20); insert(&ht, 3, 30); insert(&ht, 4, 40); insert(&ht, 6, 60); insert(&ht, 7, 70); printf("table after insert:\n"); print_table(&ht); Node *p = search(&ht, 3); if (p != NULL) { printf("search result: (%d, %d)\n", p->key, p->value); } else { printf("not found\n"); } return 0; } ``` 在这个示例中,我们定义了一个哈希表结构 `HashTable` 和一个节点结构 `Node`。每个节点包含一个键 `key`、一个值 `value`,以及一个指向下一个节点的指针 `next`。哈希表包含一个数组 `table`,其中每个元素都是一个指向节点表的指针。 我们还定义了三个函数:`hash`、`search` 和 `insert`。`hash` 函数将一个键映射到一个数组下标,`search` 函数在哈希表中查找一个键,并返回对应的节点指针,`insert` 函数向哈希表中插入一个键值对。 在 `insert` 函数中,如果该位置已经有节点,则遍历表,查找是否已经存在该键,如果存在,则更新对应的值,否则在表头插入一个新节点。如果该位置没有节点,则直接插入一个新节点。 最后,我们定义了一个 `print_table` 函数,用于打印哈希表的内容。在 `main` 函数中,我们向哈希表中插入了几个键值对,并且通过 `search` 函数查找了一个键。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值