数据结构——哈希表解析
哈希表概念
哈希表又称散列表:是根据关键字的值直接访问元素存储位置的存储结构。也就是说,在元素的存储地址和它的关键字之间建立的一个确定的对应关系H,使每个关键字和一个唯一的存储位置向对应。
散列表既是一种存储方式,也是一种查询方式。
当不同的关键字通过相同的散列函数计算得到同一地址的现象称为冲突或碰撞,称这些发生冲突的关键字相对于散列函数的同义词,在一般的情况下,冲突只能尽量减少,而不能完全避免。
构造散列函数的方法
- 1.直接定址法
- 2.折叠法
- 3.数值分析法
- 4.平方取中法
- 5.除留余数法
其中最常用的就是除留余数法,其他方法不做介绍了
除留余数法
采用取模运算%,把关键字除以某个不大于散列表长度的整数得到的余数作为散列地址。散列函数形式为:
其中,散列地址H(key)的值域[0…p-1],要求散列表的长度至少为p,若运算结果为负值,则需要加上p。
解决冲突的方法
闭散列法和开散列法
闭散列法(开放地址法)
线性探测法
线性探测法的ASL
ASL(成功) = 1 / (关键字个数) * (每个关键字的比较次数的和)
ASL(不成功) = 1 / (地址长度) * (基地址到第一个地址上关键字为空的距离)
ASL不成功解释:
看地址0,到第一个关键字为空的地址1的距离为2,因此查找不成功的次数为2.
地址1, 到第一个关键为空的地址1的距离为1,因此查找不成功的次数为1.
地址2, 到第一个关键为空的地址7的距离为6,因此查找不成功的次数为6.
地址3,到第一个关键为空的地址7的距离为5,因此查找不成功的次数为5.
地址4,到第一个关键为空的地址7的距离为4,因此查找不成功的次数为4.
地址5,到第一个关键为空的地址7的距离为3,因此查找不成功的次数为3.
地址6,到第一个关键为空的地址7的距离为2,因此查找不成功的次数为2.
地址7,到第一个关键为空的地址7的距离为2,因此查找不成功的次数为1.
地址8,到第一个关键为空的地址8的距离为1,因此查找不成功的次数为1.
地址9,到第一个关键为空的地址1的距离为5,因此查找不成功的次数为5.
地址10,到第一个关键为空的地址1的距离为4,因此查找不成功的次数为4.
开散列法(拉链法)
闭散列表的表现和实现代码:
hash.h
#pragma once
#ifndef HASH_H
#define HASH_H
#include<iostream>
using namespace std;
enum NodaState
{
EMPTY,ACTIVE,DELETED
};
template<class T>
struct Node
{
T key;
NodaState state;
Node() { state = EMPTY; }
};
template<class T>
class myHash
{
public:
myHash();
bool insertH(const T &k);
bool removeH(const T &k);
bool searchH(const T &k);
int getpos(const T &k);
int size();
int capacity();
void resize();
void print();
~myHash();
int daHash(const T & k, int maxSize);
int nextPrime(int n);
Node<T> * data;
int Maxsize;
int curl;
};
#endif // !HASH_H
hash.cpp
#include"hash.h"
template<class T>
myHash<T>::myHash()
{
Maxsize = nextPrime(15);
curl = 0;
data = new Node<T>[Maxsize];
}
template<class T>
myHash<T>::~myHash()
{
delete data;
}
template<class T>
bool myHash<T>::insertH(const T &k)
{
int offset = 1, pos;
if (curl > Maxsize / 2) resize();// 装填因子大于0.5时扩充表空间
pos = daHash(k, Maxsize);
while (data[pos].state == ACTIVE) { // 查找可用空间
if (data[pos].key != k) // 该空间被其它元素占用,发生冲突
pos = (pos + offset) % Maxsize; // 求下一个散列地址
else return false; // 该元素已经存在
} // 退出循环时
data[pos].key = k; // 保存关键字key
data[pos].state = ACTIVE; // 状态改为ACTIVE
curl++; //元素个数增加
return true;
}
template<class T>
bool myHash<T>::removeH(const T &k)
{
int pos = getpos(k); // 调用getPos求散列地址
if (pos != -1) {
data[pos].state = DELETED; // 懒惰删除,仅将标识位改为DELETED
curl--;
return true;
}
else return false;
}
template<class T>
bool myHash<T>::searchH(const T &k)
{
int offset = 1;
int pos = daHash(k, Maxsize); // 关键字为k的元素的基地址
while (data[pos].state == ACTIVE) { // 该地址处于使用中状态
if (data[pos].key != k) // pos位置的关键字不等于k
pos = (pos + offset) % Maxsize; // 计算下一个散列地址
else return true; // 关键字等于k,查找成功
}
return false;
}
template<class T>
int myHash<T>::getpos(const T &k)
{
int offset = 1;
int pos = daHash(k, Maxsize);
while (data[pos].state == ACTIVE) {
if (data[pos].key != k)
pos = (pos + offset) % Maxsize;
else return pos;
}
return -1;
}
template<class T>
int myHash<T>::size()
{
return this->curl;
}
template<class T>
int myHash<T>::capacity()
{
return this->Maxsize;
}
template<class T>
void myHash<T>::resize()
{
Node<T> *tmp = data;
int oldSize = Maxsize;
Maxsize = nextPrime(2 * oldSize);
data = new Node<T>[Maxsize];
for (int i = 0; i < oldSize; ++i) {
if (tmp[i].state == ACTIVE) {
insertH(tmp[i].key); // 执行insert会使curLength++
curl--; // 不能改变当前表长度
}
}
delete[] tmp;
}
template<class T>
void myHash<T>::print()
{
int pos;
cout << "输出闭散列表中的内容: " << endl;
for (pos = 0; pos < Maxsize; ++pos) {
if (data[pos].state == ACTIVE)
cout << pos << ": " << data[pos].key << "\t\t";
}
cout << endl;
}
template<class T>
int myHash<T>::daHash(const T & k, int maxSize)
{
int hashVal = k % maxSize;// 散列函数H(k)= k % maxSize
if (hashVal < 0) hashVal += maxSize;
return hashVal;
}
template<class T>
int myHash<T>::nextPrime(int n)
{
int i;
if (n % 2 == 0) n++;
for (; ; n += 2) {
for (i = 3; i*i <= n; i += 2)
if (n % i == 0) break;
if (i*i > n)return n;
}
}
main.cpp
#include"hash.cpp"
int main()
{
myHash<int> mh;
mh.insertH(5);
mh.insertH(15);
mh.insertH(25);
mh.insertH(35);
mh.insertH(45);
mh.insertH(55);
mh.insertH(65);
mh.print();
mh.removeH(45);
mh.print();
/*cout << mh.capacity() << endl;
cout << mh.size() << endl;
cout << mh.getpos(35) << endl;
cout << mh.searchH(75) << endl;*/
system("pause");
return 0;
}