哈希表(散列表),是通过关键字key而直接访问在内存存储位置的一种数据结构,它就是 以空间换取时间。通过多开辟几个空间,来实现查找的高效率。
对于哈希表,我们并不是很陌生:在c语言学习阶段,给定一个字符串,查找第一个只出现过一次的字符;在数据结构 矩阵的高效转置方法中,计算原矩阵中每一列中有效数字的个数;在文件压缩项目(之后给出分析总结)中,计算中文件中各个字符出现的次数。
对于构造哈希表,有以下几种方法:
1)直接定址法:取关键字key的某个线性函数为散列地址
2)除留余数法:散列地址为,key模一个小于或者等于表长的数,如果key不是整数,可以通过各种途径转换,下边介绍~~
3)折叠法
4)平方取中法
5)随机数法
6)数学分析法
下边的几种方法,只需要知道就好~~重点掌握前两种~
对于直接定址法,给定一个key,就可以对应一个地址(这种比较适用于key值比较集中的情况,如果key值过于分散,就需要浪费很多空间)
对于除留余数法,不同的key有可能对应同一个散列地址。当多个key对应一个散列地址值时,这样就发生了冲突,这种冲突叫做哈希冲突或者哈希碰撞。
既然有了冲突,我们该如何解决冲突呢???这里就会有几种方法:
1)线性探测:对于一个给定的key,当有别的key值占了它的散列地址的位置,他就应该以线性方式去找下一个空位置
2)二次探测:实际是二次方探测,当发生冲突时,以二次方的方式去找下一个空位置。
3)开链法:
下边给出以上三种办法的图形解释:
为了防止哈希冲突,我们引入了负载因子这一概念,负载因子就是哈希表中元素的个数与哈希表的大小的比值就是负载因子。对于开放定址法,载荷因子是特别重要因素,应将其控制在0.7–0.8以下,查过0.8,查表时,CPU的缓存不命中。(cache的使用,解决了主存速度不足的问题。当CPU要读取一段数据时,先在cache中查找,如果找到了,说明命中;如果找不到,去主存中查找,然后根据调度算法将要读取的数据从主存调入cache)。。
我们通常将哈希表的大小设置为素数,这样也是可以防止冲突的。
如果哈希表中的数组的容量不是素数,假设要插入的元素是10,20,30,40,哈希表的大小是10,得到1种hash值,0。如果要插入的元素是3,6,9,12,而hash表的大小是9,得到3种hash值,0,3,6。这样,就导致冲突还是比较大的。。。。。此处证明不是很严谨,只是简单说明了一下。
为了达到自动扩大容量的作用,我们哈希表的底层用vector代替数组~
下边给出实现代码:
#pragma once
#include<iostream>
using namespace std;
#include<vector>
#include<string>
//开放地址法
namespace Open
{
enum Status
{
EMPTY,
EXIST,
DELETE
};
template<typename K, typename V>
struct KVNode
{
K _key;
V _value;
Status _status;//保存某个位置的状态
KVNode(const K& key = K(), const V& value = V())
:_key(key)
, _value(value)
, _status(EMPTY)
{}
};
template<typename K>
struct __GetK
{
size_t operator()(const K& key)
{
return key;
}
};
struct __GetStrK
{
static size_t BKDRHash(const char* str)
{
unsigned int seed = 131;// 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str)
{
hash = hash * seed + (*str++);
}
return(hash & 0x7FFFFFFF);
}
size_t operator()(const string& str)
{
return BKDRHash(str.c_str());
}
};
template<typename K, typename V, typename GetK = __GetK<K>>
class HashTable
{
typedef KVNode<K, V> Node;
public:
HashTable()
:_size(0)
{
_tables.resize(2);
}
~HashTable()
{}
void Swap(HashTable<K, V, GetK>& ht)
{
swap(_size, ht._size);
_tables.swap(ht._tables);
}
bool Insert(const K& key, const V& value)
{
_CheckCapacity();
int index = _GetIndex(key, value);
while (_tables[index]._status == EXIST)
{
if (_tables[index]._key == key)//要插入的值在原表中已经存在
{
return false;
}
++index;
if (index == _tables.size())
{
index = 0;
}
}
//找到合适的位置
_tables[index]._key = key;
_tables[index]._value = value;
_tables[index]._status = EXIST;//将状态改为存在
++_size;
}
Node* Find(const K& key, const V& value)