字典的两种描述方法:跳表和散列(hash)
限制适用范围:有序数组
采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间爱你被称为散列表或者哈希表。
散列技术是在记录的存储位置和他的关键字之间建立一个确定的对应关系f(散列函数),使得每个关键字key对应一个存储位置f(key),根据这个对应关系找到给定值key的映射f(key),如果查找集合中存在这个记录,则必定在f(key)的位置上。
最常用的除法映射:f(k)=k%D
k是关键字,D是散列表的大小,f(k)对应为散列地址
hash:把任意长度的输入(预映射pre-image),通过散列算法变成固定长度的输出,该输出就是散列值,这是一种压缩映射,散列值的空间通常远远小于输入的空间,不同的输入可能会被散列成相同的输出,因此不能从散列值来唯一的确定输入值。
hash就是一种将任意长度的消息压缩到某一固定长度的消息摘要函数。
应用领域:信息安全领域的加密算法(如不同长度的信息被转换成杂乱无章的128位编码值)
线性表、树、图等结构,其中的数据元素之间存在某种逻辑关系。可以连线图示比奥斯,而散列技术的记录之间不存在逻辑关系,只是和关键字相关。散列技术既是一种存储方法也是一种查找方法。
散列主要是面向查找的存储结构,最适合查找与给定值相等的记录,
不适合范围:查找和一个关键字对应很多记录的查找,最大值最小值查找,记录的排序,限制范围查找
散列函数的构造:计算简单、散列地址分布均匀
原来的关键字-------依据某种规律变成另一组数字
直接定址法--线性条件
数字分析法---关键字的位数较大,且关键字的若干位分布较均匀
平方取中法---关键字分布未知,位数不大
折叠法 ----关键字分布未知,位数较多
随机数法------关键字长度不等时比较合适
除留余数法----关键在于选择合适的p,散列表表长为m,通常m为小于或者等于表长(最好接近)的最小指数或不包含小于20质因数的合数
散列函数选择需要考虑的因素:
计算散列地址所需要的时间
关键字的长度
散列表的大小
关键字的分布情况
记录查找的频率
散列冲突的处理:
开放定址法 一旦发生冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能被找到,记录会被存入。是一种线性探测的方法
再散列函数法
链地址法 在当前位置给单链表增加节点
公共溢出区法 冲突的关键字放在另外的公共溢出区来存放
散列表查找算法实现:
hashtable就是散列表结构、elem是一个动态数组
#define success 1
#define unsuccess 0
#define hashsize 12 //散列表长是数组的长度
#define nullkey -32768
typedef struct
{
int *elem; // 数组元素存储的基址,动态分配数组
int count; //当前数据元素个数
} hashtable;
int m=0;//散列表表长 全局变量
status InitHashTable(HashTable *H)
{
int i;
m=hashsize;
H->count =m;
H->elem=(int *)malloc(m*sizeof(int));
for (int i=0;i
H->elem[i]=nullkey;
return ok;
}
//散列函数
int hash(int key)
{
retrun key % m;//除留余数法
}
//插入关键字进行散列表
void inserthash(hashtable *H,int key)
{
int addr =hash(key);//散列地址
while(H->elem[addr]!=nullkey)
addr=(addr+1)%m;
H->elem[addr]=key;
}
//查找关键字
status searchhash(hashtable H,int key,int *addr)
{
*addr=hash(key);
while(H.elem[*addr]!=key)
{
*addr=(*addr+1)%m;
if (H.elem[*addr]==nullkey||*addr==hash(key))
{
retrun unsuccess;
}
}
return success;
}