- 模板参数
- 哈希表的迭代器实现
- 完善哈希表
- 哈希表的默认成员函数
- 引入迭代器
- 封装unordered_map和unordered_set
- unordered_set
- unordered_map
模板参数完整代码已上传至gitee:unordered_set 和 unordered_map 实现
unorder_set是K模型的容器,unorder_map是KV模型的容器
要想使用哈希表同时适配K模型和KV模型,就只能对哈希表的模板参数给出控制。同时unorder_set 和 unorder_map 需要给出解析key的仿函数KeyOfValue
框架如下:
-
哈希表 HashTable 的基本框架:
其中find,insert,erase函数已在上篇中实现,这里还需略作修改
namespace OpenHash//开散列的命名空间,防止与std冲突
{
//Key转size_t仿函数
template<class K>
struct Hash
{
const size_t operator()(const K& key)
{
return (size_t)key;
}
};
//针对string类型的key的特化
template<>
struct Hash<string>
{
const size_t operator()(const string& key)
{
size_t hash = 0;
for (size_t i = 0; i < key.size(); ++i)
{
hash *= 131;
hash += key[i];
}
return hash;
}
};
//哈希数据类型,统一为Value类型,
//Value是什么由传入的是map还是set决定
template <class Value>
struct HashData
{
//链表指针
HashData<Value>* _next;
//链表结点数据
Value _data;
HashData(const Value& data) :_data(data), _next(nullptr)
{}
};
//哈希表
template <class K, class Value, class KeyOfValue,class HashFunc = Hash<K> >
class HashTable
{
typedef HashData<Value> Node;
public:
HashTable() = default;//显示指定生成默认构造
//拷贝构造
HashTable(const HashTable& ht)
{
//...
}
//赋值重载
HashTable& operator=(const HashTable ht)
{
//...
}
//析构
~HashTable()
{
//...
}
Node* find(const K& key)
{
//...
}
bool insert(const Value& data)
{
//...
}
bool erase(const K& key)
{
//...
}
private:
vector<Node*> _table;//指针数组
size_t _n = 0;//实际存放的元素数量
};
}
- unordered_map 基本框架
namespace mystd
{
template <class K,class Value,class HashFunc=OpenHash::Hash<K>>
class unordered_map
{
struct MapKOfValue//unordered_map提供的key解析仿函数
{
//从pair中解析出key
const K operator()(const pair<const K, Value>& kv)
{
return kv.first;
}
};
public:
private:
OpenHash::HashTable<K, pair<K,Value>, MapKOfValue, HashFunc> _ht;
};
}
- unordered_set 基本框架
namespace mystd
{
template <class K,class HashFunc=OpenHash::Hash<K>>
class unordered_set
{
struct SetKOfValue//unordered_set提供的key解析仿函数
{
//直接返回key
const K& operator()(const K& k)
{
return k;
}
};
public:
private:
OpenHash::HashTable<K, K, SetKOfValue, OpenHash::Hash<K>> _ht;
};
}
一个类型做map/set的Key的要求,能支持比较大小
一个类型做unordered_map/unordered_set的Key的要求,能支持转换成整型(为了取模),并可以支持相等比较
哈希表的迭代器实现迭代器是对哈希表中元素指针的封装,以支持其移动 *** 作和解引用 *** 作。
- 迭代器基本框架
namespace mystd
{
//迭代器
template <class K, class Value, class KeyOfValue, class HashFunc = Hash<K> >
struct HashIterator
{
typedef HashData<Value> Node;
typedef HashIterator<K, Value, KeyOfValue, HashFunc> Self;
typedef HashTable<K, Value, KeyOfValue, HashFunc> HT;//迭代器需要哈希表以提供遍历
Node* _node;//结点指针
HT* _pht;//迭代器所属哈希表的地址
HashIterator(Node* node,HT* pht):_node(node),_pht(pht)
{}
};
}
重载 operator== 和 operator!=
public:
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
重载 operator * 和 operator->
public:
//返回结点数据的引用
Value& operator*()
{
return _node->_data;
}
// 返回结点数据地址
Value* operator->()
{
return &_node->_data;
}
重载operator++
步骤:
- 如果当前结点不是当前哈希桶的最后一个结点,则直接往下走;
- 如果当前结点时当前哈希桶的最后一个结点,那么要找到下一个哈希桶的第一个结点(注意边界控制)
public:
Self& operator++()
{
//桶中若还有数据,就往当前桶后面走
if (_node->_next)
{
_node=_node->_next;
}
else//当前桶走完了 换下一个桶
{
KeyOfValue kov;//解析key仿函数实例化
HashFunc hf;//key转size_t仿函数实例化
//计算当前在哪个桶
size_t index = hf((kov(_node->_data))) % _pht->_table.size();
index++;
while (index < _pht->_table.size())//防止越界
{
if (_pht->_table[index])
{
_node = _pht->_table[index];
return *this;
}
else
{
index++;
}
}
_node = nullptr;
}
return *this;
}
完善哈希表
哈希表的默认成员函数
构造函数
默认的构造函数
HashTable() = default;//显示指定生成默认构造
拷贝构造函数
由于结点类型为指针。且指针指向了开辟的空间,所以需要深拷贝
- 将新表的大小调整为旧表同样大小
- 遍历哈希桶逐个开辟空间拷贝
HashTable(const HashTable& ht)
{
_n = ht._n;
_table.resize(ht._table.size());
for (int i = 0; i < ht._table.size(); ++i)
{
Node* cur = ht._table[i];
while (cur)
{
Node* newnode = new Node(cur->_data);
//头插
newnode->_next = _table[i];
_table[i] = newnode;
cur = cur->_next;
}
}
}
赋值重载
通过传值参数的拷贝构造,置换参数临时对象和当前对象的成员变量即可
HashTable& operator=(const HashTable ht)
{
_table.swap(ht._table);
swap(_n, ht._n);
return *this;
}
析构函数
遍历结点,逐个delete
//析构
~HashTable()
{
for (size_t i = 0; i < _table.size(); ++i)
{
Node* cur = _table[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_table[i] = nullptr;
}
}
引入迭代器
迭代器实现后需要将其引入哈希表中
- 由于封装的unordered_map 和 unordered_set需要访问迭代器,所以在哈希表中,迭代器的typedef应该置于public区域中。
- 在迭代器中访问了哈希表的成员变量_table,所以需要将迭代器声明为哈希表的友元。
- 哈希表中,find函数返回类型为迭代器,insert函数的返回值将改为 pair
.
template <class K, class Value, class KeyOfValue,class HashFunc = Hash<K> >
class HashTable
{
typedef HashData<Value> Node;
template <class K, class Value, class KeyOfValue, class HashFunc>
friend struct HashIterator;// 将迭代器声明为友元
public:
typedef HashIterator< K, Value, KeyOfValue, HashFunc> iterator;//引入迭代器
//...
};
再添加begin和end函数后,改造完毕的哈希表的完整形态如下:
template <class K, class Value, class KeyOfValue,class HashFunc = Hash<K> >
class HashTable
{
typedef HashData<Value> Node;
template <class K, class Value, class KeyOfValue, class HashFunc>
friend struct HashIterator;
public:
typedef HashIterator< K, Value, KeyOfValue, HashFunc> iterator;
HashTable() = default;//显示指定生成默认构造
//拷贝构造
HashTable(const HashTable& ht)
{
_n = ht._n;
_table.resize(ht._table.size());
for (int i = 0; i < ht._table.size(); ++i)
{
Node* cur = ht._table[i];
while (cur)
{
Node* newnode = new Node(cur->_data);
//头插
newnode->_next = _table[i];
_table[i] = newnode;
cur = cur->_next;
}
}
}
//赋值重载
HashTable& operator=(const HashTable ht)
{
_table.swap(ht._table);
swap(_n, ht._n);
return *this;
}
//析构
~HashTable()
{
for (size_t i = 0; i < _table.size(); ++i)
{
Node* cur = _table[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_table[i] = nullptr;
}
}
iterator begin()
{
size_t index = 0;
while (index < _table.size())
{
if (_table[index] != nullptr)
{
return iterator(_table[index], this);
}
index++;
}
return end();
}
iterator end()
{
return iterator(nullptr, this);
}
iterator find(const K& key)
{
if (_table.size() == 0)//元素数量为0,无法查找
{
return end();
}
KeyOfValue kov;
HashFunc hf;//key转size_t 仿函数实例化
//计算哈希地址
size_t index = hf(key) % _table.size();
Node* cur = _table[index];
while (cur)//遍历哈希桶
{
if (kov(cur->_data) == key)
{
return iterator(cur,this);
}
cur = cur->_next;
}
return end();
}
pair<iterator,bool> insert(const Value& data)
{
KeyOfValue kov;
iterator ret = find(kov(data));
if (ret!=end())
{
return make_pair(ret,false);
}
HashFunc hf;//key转size_t仿函数实例化
//负载因子为1 增容
if (_n == _table.size())
{
size_t newsize = _n == 0 ? 10 : 2 * _table.size();
vector<Node*> newtable;
newtable.resize(newsize);
for (size_t i = 0; i < _n; ++i)
{
if (_table[i])//桶不空,遍历该哈希桶
{
//拿到这个桶的头结点
Node* cur = _table[i];
while (cur)
{
Node* next = cur->_next;
//重新计算映射位置
size_t index = hf(kov(cur->_data)) % newtable.size();
//插入至新表
cur->_next = newtable[index];
newtable[index] = cur;
cur = next;
}
}
}
_table.swap(newtable);//交换哈希表
}
size_t index = hf(kov(data)) % _table.size();
Node* newnode = new Node(data);
//头插
newnode->_next = _table[index];
_table[index] = newnode;
++_n;
return make_pair(iterator(newnode,this),true);
}
bool erase(const K& key)
{
KeyOfValue kov;
HashFunc hf;
//1.计算哈希地址
size_t index = hf(key) % _table.size();
//2.在桶中寻找待删除结点
Node* cur = _table[index];
Node* prev = nullptr;
while (cur)
{
if (kov(cur->_data) == key)
{
if (prev == nullptr)//cur为头结点
{
_table[index] = cur->_next;
}
else//cur为中间结点
{
prev->_next = cur->_next;
}
delete cur;
--_n;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
vector<Node*> _table;//指针数组
size_t _n = 0;//实际存放的元素数量
};
封装unordered_map和unordered_set
unordered_set
#pragma once
#include "HashTable.h"
namespace mystd
{
template <class K,class HashFunc=OpenHash::Hash<K>>
class unordered_set
{
struct SetKOfValue
{
//set使用
const K& operator()(const K& k)
{
return k;
}
};
public:
typedef typename OpenHash::HashTable<K, K, SetKOfValue, HashFunc>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
iterator Find(const K& key) {
return _ht.find(key);
}
pair<iterator,bool> Insert(const K& k)
{
return _ht.insert(k);
}
void erase(const K& key)
{
_ht.erase(key);
}
private:
OpenHash::HashTable<K, K, SetKOfValue, OpenHash::Hash<K>> _ht;
};
}
unordered_map
#pragma once
#include "HashTable.h"
namespace mystd
{
template <class K,class Value,class HashFunc=OpenHash::Hash<K>>
class unordered_map
{
struct MapKOfValue
{
//map使用
const K operator()(const pair<const K, Value>& kv)
{
return kv.first;
}
};
public:
typedef typename OpenHash::HashTable<K, pair<K, Value>, MapKOfValue, HashFunc>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
iterator Find(const K& key)
{
return _ht.find(key);
}
pair<iterator,bool> Insert(const pair<K, Value>& kv)
{
return _ht.insert(kv);
}
void erase(const K& key)
{
_ht.erase(key);
}
Value& operator[](const K& key)
{
pair<iterator, bool> ret = Insert(make_pair(key,Value()));
return ret.first->second;
}
private:
OpenHash::HashTable<K, pair<K,Value>, MapKOfValue, HashFunc> _ht;
};
}
— end —
青山不改 绿水长流
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)