哈希表
若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f()为散列函数,按这个思想建立的表为散列表(即哈希表).
对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)=f(k2)
这种现象称为碰撞
散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位.
散列函数有很多种
常用的有:
- 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。若其中H(key)中已经有值了,就往下一个找,直到H(key)中没有值了,就放进去。
- 平方取中法:当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
- 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。
同样处理碰撞问题的方法也有很多种
常用的有:
- 线性探测: 如果
k1≠k2,而f(k1)=f(k2), 就继续往后寻找, 找到一个空的位置, 然后放进去. - 链地址法: 这种方法数组的每个元素是一个链表的头指针, 如果
k1≠k2,而f(k1)=f(k2), 就往链表中头插节点.
实现代码
线性探测
/*================================================================
# File Name: hash.h
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月18日 星期五 18时26分13秒
================================================================*/
// 哈希表
#pragma once
#define TEST_HEAD printf("\n==================%s===============\n", __FUNCTION__)
#define HashMaxSize 1000
typedef int KeyType; // 要插入的值
typedef int ValueType;
// 计算下标的函数
typedef int (*HashFunc)(KeyType key);
typedef enum Stat {
Empty, // 空状态, 表示这个位置还没有元素插入
valid, // 有效状态, 表示这个位置已经有元素插入了
deleted, // 已删除状态, 表示这个位置的元素已经被删除了
} Stat;
// 键值对
typedef struct HashElem {
KeyType key;
ValueType value;
Stat stat; //每个位置的状态
} HashElem;
int func(KeyType key)
{
return key % HashMaxSize;
}
typedef struct HashTable {
HashElem data[HashMaxSize]; //哈希表数组, 存的是包含一组键值对的结构体
size_t size; //哈希表当前有效元素的个数
HashFunc func; //计算下标的函数
} HashTable;
// 初始化
void HashInit(HashTable* ht, HashFunc hash_func);
// 插入元素
void HashInsert(HashTable* ht, KeyType key, ValueType value);
// 查找
int HashFind(HashTable* ht, KeyType key, int* to_find);
// 删除
void HashRemove(HashTable* ht, KeyType key);
// 销毁
void HashDestroy(HashTable* ht);
/*================================================================
# File Name: hash.c
# Author: rjm
# mail: rjm96@foxmail.com
# Created Time: 2018年05月18日 星期五 18时25分38秒
================================================================*/
// 哈希表
#include <stdio.h>
#include "hash.h"
// 初始化
void HashInit(HashTable* ht, HashFunc hash_func)
{
ht->size = 0;
ht->func = hash_func;
for(int i=0; i<HashMaxSize; i++)
{
ht->data[i].stat = Empty;
}
return ;
}
// 插入
void HashInsert(HashTable* ht, KeyType key, ValueType value)
{
if(ht->size == HashMaxSize)
{
// 哈希表已经满了
return ;
}
int offset = func(key);
ValueType val;
if(HashFind(ht, key, &val) == 1)
return ;
if(ht->data[offset].stat != valid)
{
ht->data[offset].key = key;
ht->data[offset].value = value;
ht->data[offset].stat = valid;
++ht->size;
}
else
{
while(1)
{
offset++;
if(offset == HashMaxSize)
{
offset = 0;
}
if(ht->data[offset].stat != valid)
break;
}
h

本文介绍了哈希表的基本概念,包括通过散列函数将关键字映射到存储位置,以及处理碰撞的线性探测和链地址法。讨论了散列函数的类型,如直接寻址法、平方取中法和除留余数法,并提供了线性探测和链地址法的实现代码示例。
最低0.47元/天 解锁文章
2965

被折叠的 条评论
为什么被折叠?



