哈希表
序言
哈希表又称散列表。
对于一般的数据结构,记录在结构中的相对位置都是随机的,和记录的关键字无关,因此要想找到数据在数据结构中的相对位置需要进行一系列和关键字的”比较”。
例如顺序结构的比较的结果为==或!=,折半查找,二叉树排序树查找和B–树查找时比较的结果为<或>等等。(查找的效率依赖于查找过程中所进行的比较次数)
当然最理想的情况下,我们希望一次存取就可以查找所有元素,这就需要在每个储存位置与它的关键字之间建立一个对应关系f,使其一一对应。
哈希函数
哈希函数的设定非常灵活,他是将一个大集合映射到一个小集合,只要使得任何关键字通过哈希函数得到的哈希值都落在哈希表范围内。
下面举出了几种常见的哈西函数:
直接取值法取关键字或关键字的某个线性函数值做哈西地址,其实平常使用数组时有意无意的使用过这种函数。
H ( k e y ) = k e y 或 H ( k e y ) = a × k e y + b H(key)=key或H(key)=a×key+b H(key)=key或H(key)=a×key+b
数学分析法以r为基的数(如:以10为基的十进制数),并且哈希表中可能出现的关键字都是事先知道的,可取部分关键字的若干位组成哈希地址。(例如数据经常有重复的部分,这部分可以省去。)
平方取中法取关键字平方后的中间几位做哈系地址。有时候我们不能事先知道关键字的全部情况取其中哪几位也不合适,而一个数平方的中间几位与几乎所有位的数有关,具有很大的随机性可以作为哈西地址。
折叠法与平方取中法相似,将一个数截成几个相同的部分依次想加,这个数受所以位数的影响,具有随机性。
除留余数法这是一种最简单,也是最常用的构造哈希函数的方法,可以对关键字直接取模,也可以在折叠,平方取中后取模。(一般情况下,可以选p为质数或不包括小于20的质因数的合数)。
之后的代码样例使用了这个方法。
随机数法选择一个随机函数,取关键值的随机函数值做哈希地址。
处理冲突
当不同的关键值通过哈希函数得到了相同的哈希值是就发生了冲突。
选择好合适的哈希函数可以减少冲突,但因为是大集合向小集合是映射,所以冲突不可避免。
这就需要我们有一种遇到冲突时的处理方法。处理冲突就是找到另一个空的哈希地址。下面给出几种常见的处理冲突的方法。
开放定址法
H i = ( H ( k e y ) + d i ) M O D m i = 1 , 2... , k ( k < = m – 1 ) Hi=(H(key)+di) MOD m i = 1,2...,k(k<=m–1) Hi=(H(key)+di)MODmi=1,2...,k(k<=m–1)
di是增量序列,这里有三种取法
(1)取1,2,3,…称线性探测再散列;
(2)取12,-12,22,…称二次探测再散列;
(3)取伪随机数列,称伪随机探测再散列。
线性探测再散列会出现集聚现象,但可以保证一定可以找到还空着的哈希地址。
在哈希法
H
i
=
R
H
i
(
k
e
y
)
i
=
1
,
2
,
…
…
,
k
Hi=RHi(key) i=1,2,……,k
Hi=RHi(key)i=1,2,……,k
RHi是不同的哈希函数,这种方法不易产生“聚集”,但增加了计算时间。
链地址法
将所有的同义词储存在同一线性链表中。
建立一个公共溢出区
一旦产生冲突就填入公共溢出区。
代码
除留取余法+线性探测再散列
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
class Hashtable
{
private:
string *elem;
int size;
public:
Hashtable()
{
size = 2000;
elem = new string[size];
for (int i = 0; i < size; i++)
{
elem[i] = '#';
}
}
~Hashtable()
{
delete[] elem;
}
int hash(string &index)
{
int code = 0;
for (size_t i = 0; i < index.length(); i++)
{
code = (code * 256 + index[i] + 128) % size;
}
return code;
}
bool search(string &index, int &pos, int ×)
{
pos = hash(index);
times = 0;
while (elem[pos] != "#"&&elem[pos] != index)
{
times++;
if (times < size)
{
pos = (pos + 1) % size;
}
else
{
return false;
}
}
if (elem[pos] == index)
{
return true;
}
else
{
return false;
}
}
int insert(string &index)
{
int pos, times;
if (search(index, pos, times))
{
return 2;
}
else if (times < size / 2)
{
elem[pos] = index;
return 1;
}
else
{
recreate();
return 0;
}
}
void recreate()
{
string *temp_elem;
temp_elem = new string[size];
for (int i = 0; i < size; i++)
{
temp_elem[i] = elem[i];
}
int copy_size = size;
size *= 2;
delete[] elem;
elem = new string[size];
for (int i = 0; i < size; i++)
{
elem[i] = "#";
}
for (int i = 0; i < copy_size; i++)
{
if (temp_elem[i] != "#")
{
insert(temp_elem[i]);
}
}
delete[]temp_elem;
}
};
int main()
{
Hashtable hashtable;
string buffer;
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> buffer;
int ans = hashtable.insert(buffer);
if (ans == 0)
{
cout << "insert failed!" << endl;
}
else if (ans == 1)
{
cout << "insert success!" << endl;
}
else if (ans == 2)
{
cout << "It already exists" << endl;
}
}
int temp_pos, temp_times;
cin >> buffer;
if (hashtable.search(buffer, temp_pos, temp_times))
{
cout << "search success!" << endl;
}
else
{
cout << "search failed!" << endl;
}
system("pause");
return 0;
}