目录
operator*() operator->() operator!=()
封装unordered_map和unordered_set
哈希:
通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,我们便可以通过映射关系很快的查找到想要的元素。
哈希表(散列表):
希表(散列表)是一种数据结构,通过哈希(散列)方法,构造出来的结构称为哈希表(Hash Table)(或者称散列表),哈希表查找元素的时间复杂度是O(1)。
哈希函数:
通过哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数。
常见的哈希函数:除留余数法:设哈希表中允许的地址数为m,插入的数据为key,则:
key(关键码对应的哈希地址) = key(关键码) % m
例如:
将要插入的数据先%数组的大小,找到对应的映射位置插入
哈希冲突:
当我们在插入13时,会发现13对应的位置已经有数据了,则就构成哈希冲突
解决哈希冲突两种常见的方法是:闭散列和开散列
闭散列:
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么通过线性探测,或二次探测的方法,找到key存放到冲突位置中的“下一个” 空位置去。
闭散列的结构:
enum State //判断数组位置的状态
{
EMPTY, //空
EXITS, //存在
DELETE, //删除
};
template<class T>
struct HashData
{
T _data;
State _state; //状态
};
template<class K, class T, class KeyOfT> //仿函数,取出数据的关键字
class HashTable
{
typedef HashData<T> HashData;
public:
bool Insert(const T& d); //增
HashTable* Find(const K& key); //查
bool Erase(const K& key); //改
private:
vector<HashData> _tables; //数组
size_t _num = 0; //存了几个有效数据
};
哈希表中存放着一个数组(存放着结点)和有效数据的个数。
哈希表结点中存放的除了数据,还有一个被标记的状态,方便我们实现增删查改的操作。
插入数据
负载因子:负载因=表的数据/表的大小,衡量哈希表满的程度,表越接近满,插入数据越容易冲突,冲突越多,效率越低。
哈希表并不是满了才增容,开放定制法中,一般负载因子到0.7左右就开始增容
负载因子越小,冲突概率越低,整体效率越高,但是负载因子越小,浪费的空间越大
bool Insert(const T& data)
{
KeyOfT koft;
//表为空时增容,或者负载营造到0.7时增容,
if(_tables.size()==0 || _num*10/_tables.size()>=7)
{
//定义一个哈希表的对象,将空间开成旧表的2倍
HashTable<K, T, KeyOfT> newht;
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
newht._tables.resize(newsize);
//遍历旧表的数据,在新表中调用Insert接口,将旧表的元素依次插入的新表
for (size_t i = 0; i < _tables.size(); ++i)
{
if (_tables[i]._state == EXITS)
{
newht.Insert(_tables[i]._data);
}
}
//将新表和旧表交换
_tables.swap(newht._tables);
}
//计算出data中的key在表中映射的位置
size_t index = koft(data) % _tables.size();
//找到空的位置
while (_tables[index]._state == EXITS)
{
//如果插入的元素表中已经有了,则返回false
if(kofk(_tables[index]._date)==koft(data))
{
return false;
}
++index;
if(index==_tables.size())
{
index=0;
}
}
//找到插入的位置
_tables[index]._data