unorder_map的底层实现方法
当我每次打开网页(http://www.cplusplus.com/reference/)(C++标准库函数)的时候,查找函数的时候,我经常会看到unorder_map这个容器,,,之前不知道是什么,但是前段时间我们刚学习了 ,这个红黑树 ,,我了解到了map这个容器,,,,当时我就联想到了unorder_map这个词,但是没有深究。。。。。觉的联系不是很大。。。。
但是我们这两天我们刚刚学习了------哈希表这个数据结构,,,,,,,老师向我们提了一下,,,,,我就大概明白le。。。。。。
我们先来说说哈希表这个东西,
哈希表
哈希表是一个K_V的结构,,,,又被称为是散列表!!!!!
它是根据关键字(key)来直接访问这个内存存储数据位置的数据结构;;;;;
在构建哈希表的方法,,,,我们这里主要介绍两种方法::::
1、直接地址法;;
什么意思呢?????
就是直接取key(或者是某种线性函数)作为这个下表来存储数据 ,,,key在这里称作是 散列地址。。。
可以理解成这个样子 Hash(Key)= Key 或Hash(Key)= A*Key + B,A、B为常数。。。。
2、除留余数法;;;
这里方法的说法是:::
就是 将 key 被某个不大于散列表长m的数p除后的所得的余数为散列地址。
Hash(Key)= Key%P;;;;
但是这两种方法都有很大 的缺陷::::
比如说 :::第一种方法来说吧!!!!!
如果数据量大的话,,,,那么要建立的哈希表的长度就非常的了,,,,但是也许中间的很多的位置都没有 存取数据,,,,这样 就会造成很多的浪费。。。。所以这种方法我们经常不是很常用。。。。
如果说是,,,使用第二种方法的话,,,,也是存在问题的。。。。。。
如果,,,,要是不同的key值通过除留余数法得到的哈希地址是相同的,,,,在这里就会造成我们经常所说的哈希冲突(也就做是哈希碰撞 )这种东西 。。。。
虽然这种除留余数法有这种缺陷,,,但是 我们还是常常
使用这种方法。。。。。。所以我们想了许多的方法来解决这种 哈希冲突!!!!!!!!
哈希冲突(哈希碰撞)
解决方法::::
1、开放地址法
开放地址法,,有被称为是 闭散列法。。。。。。
(1)线性探测法:::
,,
线性探测的特点就是 如果得到的哈希地址冲突(该位置上已存储数据)的话 ,,,,我们就是将这个数据 插到下一个位置,,要是下个位置也已存储数据 ,就继续到下一个,,,直到找到正确的可以插入的数据 。。。。
代码实现 。。 。 。 。
#pragma once
#include
#include
#include
#include
enum Status//表示每个节点的状态
{
EXIST,
DELETE,
EMPTY
};
template
struct HashTableNode
{
K _key;
V _value;
Status _status;
HashTableNode(const K& key = K(),const V& value = V())
:_key(key)
,_value(value)
,_status(EMPTY)
{}
};
template
struct __HashFunc
{
size_t operator()(const K& key)
{
return key;
}
};
template<>
struct __HashFunc
{
size_t BKDRHash(const char* str)
{
register size_t hash = 0;
while(*str)
{
hash = hash*131 +*str;
++str;
}
return hash;
}
size_t operator()(const string & key)
{
return BKDRHash(key.c_str());
}
};
template>
class HashTable
{
typedef HashTableNode Node;
public:
HashTable(size_t n)
:_size(0)
{
assert(n>0);
_table.resize(n);
}
~HashTable()
{}
//插入函数
pair Insert(const K& key,const V& value = V())
{
_CheckCapacity();
Node * node = Find(key);//先查找
if(node)//如果找到的话就返回插入失败
{
return make_pair(node,false);
}
size_t idex = _HashFunc(key);//得到哈希地址
while(_table[idex]._status == EXIST)
{
idex++;//找到合适的位置
if(idex==_table.size())//如果到结尾
{
idex = 0 ;//从头重新来过
}
}//直到找到合适的位置
_table[idex]._key = key;
_table[idex]._value =value//插入数据;
_table[idex]._status = EXIST;
++_size;
return make_pair(&_table[idex],true);
}
//删除函数
bool Remove(const K& key)
{
Node* node = Find(key);//先查找数据
if(node)//找到的话
{
node->_status = DELETE;//将该位置的状态改为删除后的状态
--_size;//存入数据的个数--
return true;
}
return false;
}
//查找函数
Node * Find(const K& key)
{
size_t idex= _HashFunc(key);
while(_table[idex]._status!= EMPTY)
{
if(_table[idex]._key == key)
return &_table[idex];
++idex;
if(idex == _table.size())
idex= 0 ;
}
return NULL;
}
private:
//检查容量
void _CheckCapacity()
{
if(_table.size() == 0 )
{
_table.resize(7);
return ;
}
//如果负载因子大于 7的话,,就对 该哈希表进行扩容
if (_size*10 / _table.size() >= 7)
{
size_t newsize = _GetNextprime(_table.size());
HashTable tem(newsize);
for(size_t i = 0 ;i< _table.size();++i)
{
if(_table[i]._status == EXIST)
tem.Insert(_table[i]._key,_table[i]._value);
}
_Swap(tem);
}
}
void _Swap(HashTable& tem)
{
_table.swap(tem._table);
std::swap(_size,tem._size);
}
//得到下个素数,,,,,一般情况下,哈希表的长度都是素数 ,,,
size_t _GetNextprime(size_t n)
{
const int _PrimeSize= 28;//下面为一个素数表
static const unsigned long _PrimeList[_PrimeSize] =
{
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul,
786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul,
25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul,
805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for(size_t i = 0 ;i< _PrimeSize;++i)
{
if(n < _PrimeList[i])
return _PrimeList[i];//找到要找的下一个素数
}
assert(false);
}
//得到哈希地址
size_t _HashFunc(const K& key)
{
HashFunc hf;
size_t ret = hf(key);
return ret % _table.size();
}
private:
vector _table;//用一个vector容器来存储
size_t _size;//存储数据的个数
};
void TestHashTable()
{
HashTable ht(10);
ht.Insert(89);
ht.Insert(18);
ht.Insert(49);
ht.Insert(58);
ht.Insert(9);
ht.Insert(1);
ht.Insert(3);
ht.Insert(2);
ht.Insert(4);
ht.Insert(5);
ht.Insert(6);
ht.Insert(7);
ht.Insert(8);
ht.Insert(9);
ht.Insert(10);
ht.Remove(1);
HashTable> ht1(1);
ht1.Insert("左边","left");
ht1.Insert("左边","left");
}
除了这种方法之后 ,,我们还给了一种方法:::
(2)、二次探索法
所谓的二次探索法,,,也就是 说的是 ,,,
当遇到冲突后 ,下次所找到的位置 为 当前的位置加上 n的二次方
解决哈希冲突的方法除了这种 闭散列法之外 ,,,还有 一种开链法;;;;
开链法
所谓的开链法,,,,就是说将 产生哈希冲撞 的数据全部链到当前位置的下面 ,,
看上去就像是 ,,在下面链上了一个一个的桶子,,,,因此这样的方法我们有称作是 哈希同
实现的代码 :::
template
struct __HashFunc
{
size_t operator()(const K& key)
{
return key;
}
};
template<>
struct __HashFunc
{
size_t BKDRHash(const char* str)
{
register size_t hash = 0;
while(*str)
{
hash = hash*131 +*str;
++str;
}
return hash;
}
size_t operator()(const string & key)
{
return BKDRHash(key.c_str());
}
};
template
struct HashTableNode
{
pair _kv;//存一个pair结构
HashTableNode * _next;
HashTableNode(const pair& kv)
:_kv(kv)
,_next(NULL)
{}
};
//实现迭代器
template
struct HashTableIterator
{
typedef HashTableNode Node;
typedef HashTableIterator Self;
Node* _node;
HashTable * _ht;
HashTableIterator(Node* node,HashTable * ht)
:_node(node)
,_ht(ht)
{}
Self& operator++()
{
_node =_Next();
return *this;
}
Self operator++(int)
{
Node* cur =_node;
_node =_Next();
return cur;
}
Self& operator--()
{
_node = _Prev();
return *this;
}
Self operator--(int)
{
Node* cur =_node;
_node = _Prev();
return cur;
}
Ptr operator->()
{
return &_node->_kv;
}
Ref & operator*()
{
return _node->_kv;
}
Node* _Next()
{
if(_node->_next)
{
return _node->_next;
}
size_t idex = _ht->_HashFunc((_node->_kv).first)+1;
for(;idex < _ht->_tables.size();++idex)
{
if(_ht->_tables[idex])
{
return _ht->_tables[idex];
}
}
return NULL;
}
Node* _Prev()
{
}
bool operator!=(const Self& tem)
{
return (_node!= tem._node);
}
};
//HashFunc用来表示不同类型的key,,,求哈希地址
template>
class HashTable
{
typedef HashTableNode Node;
public:
typedef HashTableIterator&,pair*> Iterator;
typedef HashTableIterator&,const pair*> const_Iterator;
friend struct Iterator;
friend struct const_Iterator;
HashTable()
:_size(0)
{}
~HashTable()
{
Destory();
_tables.clear();
}
//清空函数
void Clear()
{
Destory();
_size =0 ;
}
//销毁函数
void Destory()
{
for(size_t i = 0 ;i< _tables.size();++i)
{
Node* cur = _tables[i];
while(cur)
{
Node* del = cur;
cur=cur->_next;
delete del;
}
_tables[i] = NULL;
}
}
HashTable(const size_t n )
:_size(0)
{
assert(n>0);
_tables.resize(n);
}
//插入函数
pair Insert(const pair& kv)
{
//检查容量
_CheckCapacity();
//先查找
Node * cur = Find(kv.first)._node;
if(cur)//找到返回失败
{
return make_pair(Iterator(cur,this),false);//返回该节点的迭代器,,并且插入失败
}
Node * node = new Node(kv);//新建节点
size_t idex = _HashFunc(kv.first);
cur = _tables[idex];
if(cur)
{
node->_next = cur;
}
_tables[idex] = node;
++_size;
return make_pair(Iterator(node,this),true);//返回新增的节点的迭代器,,,插入成功
}
//查找函数
Iterator Find(const K& key)
{
size_t idex = _HashFunc(key);//找到对应的哈希地址
Node* cur = _tables[idex];
while(cur)
{
if(cur->_kv.first == key)//找到的话
return Iterator(cur,this);//返回该位置的迭代器
cur =cur ->_next;
}
return Iterator(NULL,this);//没知道的话,,返回一个为NULL的迭代器
}
bool Erase(const K& key)
{
Node * cur =Find(key)._node;
if(cur)
{
size_t idex = _HashFunc(key);
if(_tables[idex] == cur)
{
delete cur ;
_tables[idex] = NULL;
}
else
{
Node* parent = _tables[idex];
while(parent->_next != cur)
{
parent->_next = cur->_next;
delete cur;
}
}
--_size;
return true;
}
return false;
}
Iterator Begin()
{
for(size_t i =0 ;i< _tables.size();++i)
{
Node* cur = _tables[i];
if(cur)
{
return Iterator(cur,this);
}
}
return Iterator(NULL,this);
}
const_Iterator Begin()const
{
for(size_t i =0 ;i< _tables.size();++i)
{
Node* cur = _tables[i];
if(cur)
{
return Iterator(cur,this);
}
}
return Iterator(NULL,this);
}
const_Iterator End()const
{
return Iterator(NULL,this);
}
Iterator End()
{
return Iterator(NULL,this);
}
//使用迭代器输出哈希表内的数据
void Print()
{
Iterator it =Begin();
while(it!= End())
{
cout<first<<" ,"<second< n)//找到下一个素数
{
return _PrimeList[i];
}
}
return _PrimeList[_PrimeSize-1];//如果要是已经是最大了,,就直接返回最后的值
}
//检查容量 ,,,判断是否需要来扩容
void _CheckCapacity()
{
if(_tables.size() == _size)//如果负载因子为1的话,就要进行 扩容
{
size_t newsize = _GetNextPrime(_tables.size());//得到下一步 扩容 后的哈希表的长度
HashTable tem(newsize);//生成一个新长度的哈希表
for(size_t i = 0 ;i < _tables.size();++i)//将原哈希表内的数据全部,,,插入到生成的这个对象中
{
Node* cur = _tables[i];
while(cur)
{
tem.Insert(cur->_kv);
cur = cur->_next;
}
}
_Swap(tem);//插入结束后 ,,,我们就将原哈希表的内容与生成的新对象内容 交换
}
}
//用来交换两个HashTable的对象内容
void _Swap(HashTable& tem)
{
_tables.swap(tem._tables);
std::swap(_size,tem._size);
}
//获得
size_t _HashFunc(const K& key)
{
HashFunc hf;
size_t ret = hf(key);
return ret % _tables.size();
}
protected:
vector _tables;//哈希表的内部不存数据,存储的是所链节点的第一个指针
size_t _size;//存储节点的数量
};
关于 哈希表的知识就大概这么多了 ;;;
那么我们下面 就来 说 说 unorder_map这个东西,,,在系统库中 的unorder_map这个容器 就是使用的这个 哈希表来实现的,,,并且所谓的哈希桶来实现的。。。。
下面就来实现一下吧 !!!!
template>
class UnorderMap
{
typedef HashTable Hash;
public:
typedef typename Hash::Iterator Iterator;
typedef typename Hash::const_Iterator const_Iterator;
UnorderMap()
{}
//插入函数
pair Insert(const pair & kv)
{
return _ht.Insert(kv);//直接调用 hash表的函数
}
bool Erase(const K& key)
{
return _ht.Erase(key);//调用 删除函数
}
Iterator Find(const K& key)
{
return _ht.Find(key);//调用 查找函数
}
Iterator Begin()
{
return _ht.Begin();//直接调用
}
const_Iterator Begin()const
{
return _ht.Begin();
}
Iterator End()
{
return _ht.End();
}
const_Iterator End()const
{
return _ht.End();
}
void Print()
{
_ht.Print();
}
protected:
Hash _ht;
};