C++ HashMap

来源:互联网 发布:2017 大数据 大会 编辑:程序博客网 时间:2024/06/05 08:05

C++ HashMap

参考自boost库。

基本原理:
使用连续的内存块(数组)来存储数据,通过一个hash函数(即使每一个元素值都能形成固定的数组下标值),然后数组在相应的位置存起来,不能保证每个元素的关键值与hash函数值是一一对应的(可以有多个元素生成一样的hash值),这时候就需要解决冲突。
我们把数组叫做桶(因为数组把很多相似的元素存放在同一个位置)。
流程:
查找元素
- 元素的关键值通过hash函数计算得到hash值
- 得到桶号(数组下标位置)
- 比较桶的元素值(因为只有一次hash函数的值并不准确)
- 取出相同的元素的关键值,不相等则结束

插入元素
- 元素的关键值通过hash函数计算得到hash值
- 得到桶号(数组下标位置)
- 存放元素

我们需要一个提供hash函数的工具。写出一个行为类,重载()运算符。

class CalculateHashCode{public:  std::size_t operator()(const string& str)  {    std::size_t n = 0;    std::size_t nIndex = str.length();    while (nIndex) {      n <<= 5;      if ((--nIndex)& 1) {        n += str[nIndex] * 3;      }      else {        n ^= ((n >> 17) | 0x01) * str[nIndex];      }    }    return n;  }  std::size_t operator()(const wstring& str)  {    std::size_t n = 0;    std::size_t nIndex = str.length();    while (nIndex) {      n <<= 5;      if ((--nIndex) & 1) {        n += str[nIndex];      }      else {        n ^= ((n >> 17) | 0x01) * str[nIndex];      }    }    return n;  }  std::size_t operator()(const short& nNum)  {    return nNum;  }  std::size_t operator()(const unsigned short& nNum)  {    return nNum;  }  std::size_t operator()(const char& nNum)  {    return nNum;  }  std::size_t operator()(const unsigned char& nNum)  {    return nNum;  }  std::size_t operator()(const size_t& nNum)  {    return nNum;  }  std::size_t operator()(const int& nNum)  {    return nNum;  }  std::size_t operator()(const long long& nNum)  {    return (std::size_t)(nNum >> 31 ^ nNum);  }  std::size_t operator()(const unsigned long long& nNum)  {    return (std::size_t)(nNum >> 31 ^ nNum);  }  std::size_t operator()(const float& nNum)  {    return (std::size_t)(nNum * 50331653.333333f);  }  std::size_t operator()(const double& nNum)  {    return (std::size_t)(nNum * 50331653.333333);  }  std::size_t operator()(const long double& nNum)  {    return (std::size_t)(nNum * 50331653.333333);  }};
HashMap:
hashMap应该是一个泛型容器,这里使用类模板来抽象数据类型。当然啦,也可以用void*来做出泛型。
template<typename Key,   typename Value,   typename CalcHash = CalculateHashCode>

三个类型参数名分别是元素的关键字,元素的内容值和计算hash的工具类型,这里我们默认为提供工具类型的是我们所写的计算hash类。

数据成员:

  typedef struct _tagBucket  {    iterator _first; //list某一个桶的开始    iterator _last;  //list某一个桶的最后一个元素  }Bucket;  list<ValueType> _listValues;  Bucket* _arrBuckets;  std::size_t _nBucketSize;  CalcHash _calcHash;

_listValues。使用标准库的双向链表来存放全部数据,为什么这样做呢?前面我们说过了当元素关键字冲突,计算到了一个被使用桶时候,这时候需要解决冲突问题,这里我们用将元素插入链表来解决,该方法称为链地址法。
_arrBuckets。 一个我们自定义的数据类型“Bucket”,我们只存放链表的区间范围的迭代器,这样就不需要为每一个桶都做一个链表,而是将每一个桶都想象为一个链表的某一个区间。
_nBucketSize。 桶的大小。
_calcHash。 计算hash值。

供外部使用的类型:

  typedef typename pair<Key, Value> ValueType;  typedef typename list<ValueType>::iterator iterator;  typedef typename  list<ValueType>::const_iterator const_iterator;

ValueType。标准库里,一对出现的元素都是用pair来解决的,我们也一样。
iterator。遍历链表元素。
const_iterator。遍历链表const元素。
内部方法:
我们将使用hash函数值的函数都是为私有的方法。

  iterator find(const Key& k, std::size_t nHashCode);  Value& at(const Key& k, std::size_t nHashCode);  pair<iterator, bool> insert(const ValueType& v, std::size_t nHashCode);  void erase(iterator it, std::size_t nHashCode);  void reHashSize(std::size_t nHashBucketSize);  static std::size_t hashSize(std::size_t nNumber);//静态函数

find方法:通过一个元素关键字来查找,需要一个hash值,成功返回元素迭代器,失败放回end迭代器。
at方法:通过一个元素关键字来查找,需要一个hash值,成功返回元素的内容,失败插入一个相同关键字并初始化内容的元素,然后返回元素的内容。
insert:插入一个元素,需要一个hash值。不允许重复关键字,成功返回该元素迭代器,bool值为true,失败返回end迭代器,bool值为false。
erase:该元素的迭代器,需要一个hash值(需要知道桶的位置)。
reHashSize:该容器可以扩容。需要一个桶的大小值。
hashSize:通过一个值,提供一个等于该值或者大于该值的值。(提供合适的值,提供素数是不错的办法)

供外部使用的方法:

  HashMap()    :_calcHash(CalcHash()), _listValues(), _nBucketSize(0), _arrBuckets(nullptr)  {    reHashSize(hashSize(1));  }  HashMap(const CalcHash& func)    :_calcHash(func), _listValues(), _nBucketSize(0), _arrBuckets(nullptr)   {    reHashSize(hashSize(1));  }  HashMap(const HashMap<Key, Value, CalcHash>& hashMap)    :_calcHash(hashMap._calcHash), _listValues(hashMap._listValues), _nBucketSize(0), _arrBuckets(nullptr)  {    reHashSize(hashSize(_listValues.size()));  }  HashMap(const initializer_list<ValueType>& list, const CalcHash& func = CalcHash())    :_calcHash(func), _listValues(list), _nBucketSize(0), _arrBuckets(nullptr)  {    reHashSize(hashSize(_listValues.size()));  }  ~HashMap()  {    if (_arrBuckets)      delete[] _arrBuckets;  }  std::size_t BucketSize()  {    return _nBucketSize;  }    iterator begin()  {    return _listValues.begin();  }  iterator end()  {    return _listValues.end();  }  const_iterator cbegin() const  {    return _listValues.begin();  }  const_iterator cend() const  {    return _listValues.end();  }  bool empty()  {    return _listValues.empty();  }  Value& at(const Key& k)  {    if ((_listValues.size()) >= (_nBucketSize << 1))      reHashSize(hashSize(_listValues.size()));    return at(k, calcHashCode(k));  }  HashMap<Key, Value, CalcHash>& operator<<(const ValueType& v)  {    at(v.first) = v.second;    return *this;  }  Value& operator[](const Key& k)  {    return at(k);  }  iterator find(const Key& k)  {    return find(k, calcHashCode(k));  }  void erase(iterator it)  {    erase(it, calcHashCode(it->first));  }  void erase(const Key& k)  {    std::size_t nHashCode = calcHashCode(k);    erase(find(k, nHashCode), nHashCode);  }  pair<iterator, bool> insert(const ValueType& v)  {    if ((_listValues.size()) >= (_nBucketSize << 1))      reHashSize(hashSize(_listValues.size()));    return insert(v, calcHashCode(v.first));  }  void clear()  {    _listValues.clear();    iterator end = _listValues.end();    for (size_t i = 0; i != num_buckets_; ++i)      buckets_[i].first = buckets_[i].last = end;  }

大家,都自己实现自己的HashMap吧!

1 0
原创粉丝点击