前言
理想的搜索方式:可以不经过任何比较,一次直接从表中得到要搜索的元素,构造一种结构,通过某种函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么查找时就能通过函数一下找到该元素
哈希
1.本质上是一个数组,通过哈希函数使元素关键码和元素存储位置有一定的映射关系
2.搜索某一元素时,通过哈希函数计算出元素存储位置,在数组中按此位置取元素比较,相等即存在
3.插入,通过哈希函数计算出元素的存储位置并按这个位置进行存储
4.存在哈希冲突,两个不同元素通过哈希函数所映射出的存储位置相同
哈希冲突
哈希冲突避免不了,解决哈希冲突问题常见的 俩方法:闭散列,开散列
闭散列
又名开放定址法,当发生哈希冲突时,如果哈希表没有满,将带插入元素key插入到下一空位去
如何找到下一个空位
处理方式:从发生冲突的位置开始,依次继续往后探测,直到找到空位置
插 入:用哈希函数找到带插入元素的存储位置,如果该位置没有元素,则直接插入,如果该位置有元素但不是带插入元素就发生了哈希冲突,使用线性探测找到下一个空位置,插入
不可删除:用闭散列时,不能随便删除哈希表中存在的元素,如果直接删除元素会影响其他元素的搜索【可以引用计数走一波】
缺 点:发生哈希冲突,冲突都连在一起,不同的元素占用了空位置,使寻找某元素的位置需要多次比较,查找效率降低
•负载因子
【填入表中的元素个数/散列表的长度】
因为散列表的长度是固定的,所以负载因子越大,表中的数据越多,产生冲突的可能性就越大,开放定址的负载因子严格控制在0.7-0.8
开散列
又名链地址法,开链法,用散列函数计算散列地址,具有相同的元素属于同一子集,每个子集是一个桶,每个桶通过一个单链表连接起来,每个链表的头结点存储在哈希表中
头文件定义
#pragma once
#include<god.h>
typedef int KeyType;
typedef int ValueType;
//枚举
typedef enum Status
{
EMPTY,//空
EXITS,//存在
DEL //删除
}Status;
//hash结点的定义
typedef struct HashNode
{
KeyType _key;
ValueType _value;
Status _status;
}HashNode;
//定义一个结构体方便管理hash结点,形成hash表
typedef struct HashTable
{
HashNode* _tables;
int _size;
int _N;
}HashTable;
//打印hash表
int HashTablePrint(HashTable* ht);
//初始化hash表
void HashTableInit(HashTable* ht, int n);
//hash表的插入
int HashTableInsert(HashTable** ppht, KeyType key, ValueType value);
//hash表查找
HashNode* HashTableFind(HashTable* ht, KeyType key);
//hash表移除某个结点
int HashTableRemove(HashTable* ht, KeyType key);
//hash表的销毁
int HashTableDestory(HashTable* ht);
代码各个功能解析
代码使用的方法是闭散列,即开发定址法
初始化
初始化ht指向的空间,为ht的_tables开辟内存用来储存数据,并初始化_size和_N,_N代表总容量,_size代表当前已经储存的数据的个数,初始化时将_size赋0,所有结点的状态都初始化为EMPTY