写一个简单的哈希桶
定义HashNode 里面包含pair
struct HashNode
{
typedef HashNode Node;
HashNode(const pair& kv)
:_kv(kv)
,_next(nullptr)
{}
pair _kv;
Node* _next;
};
为什么用pair
template
struct HashNode
{
typedef HashNode Node;
HashNode(const pair& kv)
:_kv(kv)
,_next(nullptr)
{}
pair _kv;
Node* _next;
};
template
class HashTable
{
typedef HashNode Node;
public:
HashTable()
:_n(0)
{
_ht.resize(10, nullptr);
}
bool Insert(const pair& kv)
{
//check if exist
if (Find(kv.first))
{
return false;
}
// add space
if (_n == _ht.size())
{
size_t newsize = _ht.size() * 2;
HashTable newht;
newht._ht.resize(newsize, nullptr);
for (int i = 0; i < _ht.size(); i++)
{
Node* cur = _ht[i];
while (cur)
{
newht.Insert(cur->_kv);
cur = cur->_next;
}
_ht[i] = nullptr;
}
swap(newht, *this);
}
//position
size_t hashi = kv.first % _ht.size();
Node* newnode = new Node(kv);
if (_ht[hashi])
{
//head_insert
newnode->_next = _ht[hashi];
_ht[hashi] = newnode;
}
else
{
_ht[hashi] = newnode;
}
++_n;
return true;
}
Node* Find(const K& key)
{
size_t hashi = key % _ht.size();
Node* cur = _ht[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
return cur;
}
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
if (Find(key)==nullptr)
{
return false;
}
size_t hashi = key % _ht.size();
Node* cur = _ht[hashi];
Node* before = nullptr;
while (cur)
{
if (cur->_kv.first == key)
{
if (before == nullptr)
{
_ht[hashi] = _ht[hashi]->_next;
}
else {
before->_next = cur->_next;
}
delete cur;
--_n;
return true;
}
before = cur;
cur = cur->_next;
}
return false;
}
void print()
{
for (int i = 0; i < _ht.size(); i++)
{
printf("[%d]", i);
Node* cur = _ht[i];
while (cur)
{
cout << "->";
cout<_kv.first;
cur = cur->_next;
}
cout << "->";
printf("null\n");
}
printf("\n");
}
private:
vector _ht;
size_t _n = 0;
};
实际上Find和Erase的参数可以用pair
进一步抽象,如果我们不仅要封装map和set还有其它封装,就必须更抽象,比如数据的部分不能指定是pair
template
struct HashNode
{
typedef HashNode Node;
HashNode(const T& data)
:_data(data)
,_next(nullptr)
{}
T _data;
Node* _next;
};
定义泛型T _data,如果要封装map和set那么必然这个data要有key,value值。
HashTable依然保留K key,按理来说如果需要Key可以从data里面从提取,但依然保留是表示这是一个Key/Value模型。同时保留了Key方便Erase和Find处理
template
class HashTable
{
typedef HashNode Node;
// ...
}
前面说了data是个泛型,而代码的key有两个作用:第一个是映射位置,当它是整型是可以除余法,如果是string要定义规则,通过key的比较来实现Find()。
会有两个仿函数
// key->pos
struct Hashfunc
{};
// data->key
struct KeyOfT
{};
template
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
// 特化
template<>
struct HashFunc
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
struct KeyOfT
{
const K& operator()(const pair& data)const
{
return data.first;
}
};
完整代码:
Hashtabletree.h
namespace hash_bucket
{
template
struct HashNode
{
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
T _data;
HashNode* _next;
};
template
class HashTable
{
public:
typedef HashNode Node;
typedef HashTable hashtable;
KeyOfT kt;
HashFunc hf;
HashTable()
:_n(0)
{
_ht.resize(10, nullptr);
}
bool Insert( T& data)
{
//check if exist
if (Find(kt(data)))
{
return false;
}
// add space
if (_n == _ht.size())
{
size_t newsize = _ht.size() * 2;
hashtable newht;
newht._ht.resize(newsize, nullptr);
for (int i = 0; i < _ht.size(); i++)
{
Node* cur = _ht[i];
while (cur)
{
newht.Insert(cur->_data);
cur = cur->_next;
}
_ht[i] = nullptr;
}
swap(newht, *this);
}
//position
size_t hashi = hf(kt(data)) % _ht.size();
Node* newnode = new Node(data);
if (_ht[hashi])
{
//head_insert
newnode->_next = _ht[hashi];
_ht[hashi] = newnode;
}
else
{
_ht[hashi] = newnode;
}
++_n;
return true;
}
Node* Find(const K& key)
{
size_t hashi = hf(key) % _ht.size();
Node* cur = _ht[hashi];
while (cur)
{
if (kt(cur->_data) == key)
{
return cur;
}
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
if (Find(key)==nullptr)
{
return false;
}
size_t hashi = hf(key) % _ht.size();
Node* cur = _ht[hashi];
Node* before = nullptr;
while (cur)
{
if (kt(cur->_data) == key)
{
if (before == nullptr)
{
_ht[hashi] = _ht[hashi]->_next;
}
else {
before->_next = cur->_next;
}
delete cur;
--_n;
return true;
}
before = cur;
cur = cur->_next;
}
return false;
}
void print()
{
for (int i = 0; i < _ht.size(); i++)
{
printf("[%d]", i);
Node* cur = _ht[i];
while (cur)
{
cout << "->";
cout<< kt(cur->_data);
cur = cur->_next;
}
cout << "->";
printf("null\n");
}
printf("\n");
}
private:
vector _ht;
size_t _n = 0;
};
}
my_unorded_set.h
#include"Hashtable.h"
namespace my_set
{
template
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
// 特化
template<>
struct HashFunc
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
template
class set
{
public:
struct KeyOfT
{
const K& operator()(const pair& data)const
{
return data.first;
}
};
bool Insert(const K& key)
{
pair newpair = make_pair(key, key);
return _ht.Insert(newpair);
}
bool Erase(const K& key)
{
return _ht.Erase(key);
}
void Print()
{
_ht.print();
}
private:
hash_bucket::HashTable, KeyOfT, HashFunc> _ht;
};
}
目前为止我们来个总结
两步
第一把具体的pair
变成抽象的data 第二步:创建两个仿函数,作用分别是提取Key和从Key计算pos。(仿函数的出现往往意味着适配,比如正因为key不一定为整型才需要仿函数
封装的迭代器:
在哈希桶实现迭代器不限制权限。
template
class HashTable;
template
struct HTIterator
{
typedef HashNode Node;
typedef HTIterator Self;
Node* _node;
HashTable* _pht;
HTIterator(Node* node, HashTable* pht)
:_node(node)
, _pht(pht)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
Self& operator++()
{
//下一个不为空
if (_node->_next)
{
_node = _node->_next;
return *this;
}
else
{
KeyOfT kt;
HashFunc hf;
size_t hashi = hf(kt(_node->_data)) % (_pht->_ht).size();
hashi++;
while (hashi < _pht->_ht.size())
{
_node = _pht->_ht[hashi];
if (_node != nullptr)
{
return *this;
}
hashi++;
}
}
_node = nullptr;
return *this;
}
bool operator==(const Self& it)const
{
return _node == it._node;
}
bool operator!=(const Self& it)const
{
return _node != it._node;
}
};
模板类的声明要带着模板参数把内容再写一遍。
迭代器友元类的声明
friend struct HTIterator;