在STL中,unordered_map与unordered_set的查找是天花板级别的,他们的底层逻辑很简单,就是哈希捅,哈希捅的逻辑十分简单,就是不一样的链表和数组而已(个人认为)。但是千万不要绝的实现unordered_map与unordered_set很简单,就像做饭一样,我们每个人都可以做出挂面,但是最后吃进嘴的感觉,味道是不一样,大厨做出来的大概率会比一个做饭小白做的好吃,而unordered_map与unordered_set就像做饭一样,想实现哈希捅不难,但是封装接口是真的难。下面是代码:
//unordered_map头文件
#pragma once
#include"Hash.h"
namespace myunmap
{
template<class K,class V>
class myunm
{
public:
struct map;
typedef Hash::HashNode<pair<K,V>> node;
typedef typename Hash::Iterator<K, pair<K,V>, map,pair<K,V>&, pair<K,V>*, Hash::HashFunc<K>> iterator;
struct map
{
const K& operator()(const pair<K,V>& x)
{
return x.first;
}
};
pair<iterator,bool> insert(const pair<K, V>& x)
{
return _Table.insert(x);
}
node* find(const K& x)
{
return _Table.find(x);
}
iterator begin()
{
return _Table.begin();
}
iterator end()
{
return _Table.end();
}
V& operator[](const K& data)
{
pair<iterator, bool> ret = _Table.insert(make_pair(data, V()));
return ret.first->second;
}
private:
Hash::HashTable<K,pair<K,V>,map,Hash::HashFunc<K>> _Table;
};
}
//unordered_set头文件
#pragma once
#include "Hash.h"
namespace myunset
{
template<class K>
class myset
{
public:
struct set;
typedef Hash::HashNode<K> node;
typedef typename Hash::Iterator<K, K, set, K&, K*, Hash::HashFunc<K>> iterator;
struct set
{
const K& operator()(const K& x)
{
return x;
}
};
pair<iterator,bool> insert(const K& x)
{
return _Table.insert(x);
}
node* find(const K& x)
{
return _Table.find(x);
}
iterator begin()
{
return _Table.begin();
}
iterator end()
{
return _Table.end();
}
private:
Hash::HashTable<K,K,set,Hash::HashFunc<K>> _Table;
};
}
//Hash.h头文件
#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<set>
using namespace std;
namespace Hash
{
template<class K>
struct HashFunc;
template<class T>
struct HashNode;
template<class K, class T, class KofT, class Hashf>
class HashTable;
template<class K ,class T,class KofT, class ref, class ptr,class Hashf = Hash::HashFunc<K>>
struct Iterator
{
typedef HashNode<T> node;
typedef HashTable<K, T, KofT, Hashf> HT;
node* ret;
HT* _ht;
Iterator(node* x,HT* ht)
:ret(x)
,_ht(ht)
{}
ref operator*()const
{
return ret->_data;
}
ptr operator->()const
{
return &ret->_data;
}
Iterator& operator++()
{
if (ret->_next)
ret = ret->_next;
else
{
KofT kot;
Hashf hsf;
size_t pp = hsf(kot(ret->_data)) % _ht->_table.size();
for (size_t i = pp + 1; i < _ht->_table.size(); i++)
{
if (_ht->_table[i])
{
ret = _ht->_table[i];
return *this;
}
}
ret = nullptr;
}
return *this;
}
bool operator!=(const Iterator<K, T, KofT, ref, ptr, Hashf>& x)const
{
return ret != x.ret;
}
};
template<class K>
struct HashFunc
{
const K& operator()(const K& key)
{
return key;
}
};
template<>
struct HashFunc<string>
{
size_t operator()(const string& key)
{
size_t val = 0;
for (auto e : key)
{
val += e;
val *= 131;
}
return val;
}
};
template<class T>
struct HashNode
{
T _data;
HashNode<T>* _next;
HashNode(const T& val)
:_data(val)
,_next(nullptr)
{}
};
template<class K ,class T , class KofT , class Hashf = Hash::HashFunc<K>>
class HashTable
{
typedef HashNode<T> node;
template<class K, class T, class KofT, class ref, class ptr, class Hashf>
friend struct Iterator;
typedef Iterator<K, T, KofT,T&,T*,Hashf> iterator;
public:
HashTable()
{
_table.resize(3);
}
iterator begin()
{
for (size_t i = 0; i < _table.size(); i++)
{
node* cur = _table[i];
if (cur)
return iterator(cur, this);
}
return iterator(nullptr, this);
}
iterator end()
{
return iterator(nullptr, this);
}
pair<iterator,bool> insert(const T& x)
{
//去重
if (find(_kot(x)))
return make_pair(iterator(find(_kot(x)),this), false);
if (_size != 0 && _size / _table.size() == 1)
{
vector<node*> tmp;
tmp.resize(2 * _table.size(), nullptr);
for (size_t i = 0; i < _table.size(); i++)
{
if (_table[i])
{
node* cur = _table[i];
while (cur)
{
node* next = cur->_next;
size_t ret = hsf(_kot(cur->_data))% tmp.size();
cur->_next = tmp[ret];
tmp[ret] = cur;
cur = next;
}
}
}
_table.swap(tmp);
}
int ret = hsf(_kot(x)) % _table.size();
node* cur = new node(x);
cur->_next = _table[ret];
_table[ret] = cur;
_size++;
return make_pair(iterator(cur, this), true);
}
node* find(const K& key)
{
if (_table.size() == 0)
return nullptr;
size_t ret = hsf(key) % _table.size();
if (_table[ret])
{
node* cur = _table[ret];
while (cur)
{
if (_kot(cur->_data) == key)
return cur;
cur = cur->_next;
}
}
return nullptr;
}
private:
vector<node*> _table;
size_t _size = 0;
KofT _kot;
Hashf hsf;
};
}
//测试代码
#include"Hash.h"
#include"unordered_map.h"
#include"unordered_set.h"
int main()
{
/*Hash::HashTable<int> s;
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(1);*/
myunmap::myunm<int, int> s;
vector<int> v = { 1,1,1,1,1,1,1,1 };
//myunmap::myunm<int, int>::iterator sit = s.begin();
for (auto e : v)
{
s[e]++;
}
for (auto& e : s)
cout << e.first << endl;
/*myunset::myset<int> h;
h.insert(1);
h.insert(2);
h.insert(3);
h.insert(4);
h.insert(5);
h.insert(6);
h.insert(7);
myunset::myset<int>::iterator sit = h.begin();
while (sit != h.end())
{
cout << (*sit) << endl;
++sit;
}*/
return 0;
}
怎么说,实现哈希捅的时候,前前后后大概用了一个小时左右,封装接口用了三个小时左右,封装代码的过程怎么说,难受,其实在封装其他的时候,感觉还可以,但是到迭代器的那一块,真的是感觉绕糊了,为什么说很绕呢?其实也怪我写的位置,在最开始,所以把下面所有的类都声明了一遍。所以导致看起来很乱。行了,说说怎么实现的吧。
其他地方就不说了,主要是迭代器那一块。
迭代器的模版传参数,我刚开始写的是K,K&,K*。但是这样写你会发现一个问题,那就是没有哈希捅的指针,而要实现迭代器,哈希捅的指针是必不可少的,所以,这就很矛盾。所以不可以这样设置参数。因为要用哈希捅的指针,所以哈希捅的四个模版参数不能少,其实如果没有啥的话,就可以实现了。但是我又加了两个参数,那就是ref,ptr。这样就可以方便一些。好了,哈希捅的指针问题解决之后,就相当于是迭代器中的复杂问题都解决了。但是下一个问题又来了,那就是operator[]怎么办?这个重载函数的参数应该是K,但是你要插入的话,像unordered_map的插入是pair啊,所以,此时就必须要在unordered_map的这个文件中,调用Hash文件中的插入,在传下去。
以上就是两个难点,虽然说起来简单,但是自己实现的话,我觉还是有点难度的,实现封装比实现哈希捅难多了,尤其是往下传这个过程,特别绕。我个人认为,map,set的封装都比unordered_map,unordered_set简单,主要是unordered_map,unordered_set这两个是要用哈希函数来映射的,所以必须要支持取余,但是如果是string呢?string的话就不用自己实现的了?所以此时需要一个仿函数。而map,set不需要,红黑树是我只要知道你是pair还是K就好了。所以只要一个仿函数就可以了。
最后,希望大家支持一下吧!!