目录
一、前言
二、容器的使用
1、unordered_map
2、unordered_set
编辑
三、哈希表的改造
1、结点
2、哈希表的迭代器
* 构造函数
* 重载 *
* 重载 ->
* 重载 ++
* 重载 != 和 ==
3、哈希表的析构
4、unordered_map的 [] 实现
5、修改后的哈希表
四、 unordered_set的实现
五、unordered_map的实现
在C++11中,STL提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,但是效率却提高了。
unordered系列的关联式容器之所以效率比较高,是因为unordered系列的关联式容器的底层使用了我们前面所讲到的哈希结构。那么今天我们就来看看 unordered_set和unordered_map这两个容器的使用及其实现。
1、 unordered_map是存储
键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
2、 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
3、 在内部unordered_map没有对按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。
4、 unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。
5、unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。
int main()
{
unordered_map countMap;
string arr[] = { "苹果","香蕉","苹果","葡萄","西瓜" };
for (auto& e : arr)
{
auto it = countMap.find(e);
/*if (it == countMap.end())
{
countMap.insert(make_pair(e, 1));
}
else
{
it->second++;
}*/
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
return 0;
}
从运行结果我们可以看出 unordered_map里面的数据是无序且数据是可以有重复的。
1、不再以键值对的形式存储数据,而是直接存储数据的值。
2、容器内部存储的各个元素的值都互不相等,且不能被修改。
3、不会对内部存储的数据进行排序。
int main()
{
unordered_set us;
us.insert(10);
us.insert(1);
us.insert(10);
us.insert(3);
us.insert(4);
us.insert(4);
auto it = us.begin();
while (it != us.end())
{
cout << *it << " ";
it++;
}
cout << endl;
return 0;
}
从运行结果我们可以看出 unordered_set里面的数据是无序且没有重复的。
从标准库中我们知道,unordered_set和unordered_map这两个容器的底层结构都是哈希表。但是两个容器所存储的数据类型不同,前者存储的是 key类型的数据,后者存储的是 key/value类型的数据。所以为了方便,我们需要像实现 map和set的底层红黑树那样,去实现一个通用的哈希表。所以我们需要对哈希表改造一下,来满足两个容器都可以使用。
首先,我们需要对节点改造,使其能够满足两种存储方式。
template
struct HashNode
{
T _data;
HashNode* _next;
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
};
我们需要对哈希表指针和结点指针进行封装。
template
class HashTable;
template
struct _HashIterator
{
typedef HashNode Node;
typedef HashTable HT;
typedef _HashIterator Self;
Node* _node;
HT* _ht;
};
_HashIterator(Node* node, HT* ht)
:_node(node)
, _ht(ht)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
思路:如果当前的哈希桶还有节点(即下一个结点不为空),那么++就是当前桶的当前节点的下一个节点,如果当前元素是所在桶的最后一个元素,那么++就是找下一个非空的哈希桶。
Self& operator++()
{
if (_node->_next)
{
//当前桶迭代
_node = _node->_next;
}
else
{
// 找下一个桶
Hash hash;
KeyOfT kot;
size_t i = hash(kot(_node->_data)) % _ht->_tables.size();
i++;
for (; i < _ht->_tables.size(); i++)
{
if (_ht->_tables[i])
{
_node = _ht->_tables[i];
break;
}
}
//说明后面没有有数据的桶了
if (i == _ht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
bool operator!=(const Self& s)const
{
return _node != s._node;
}
bool operator==(const Self& s)const
{
return _node == s._node;
}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
_tables[i] = nullptr;
}
}
我们知道,unordered_map的操作中可以通过[]来访问数据,并修改value值,那么这是怎么做到的呢?
其实要想实现[],我们需要先把insert的返回值修改成pair
pair insert(const T& data)
{
Hash hash;
KeyOfT kot;
//去重
iterator ret = Find(kot(data));
if (ret != end())
{
return make_pair(ret, false);
}
//扩容
if (_size == _tables.size())
{
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 10;
vector newTables;
newTables.resize(newsize, nullptr);
//将旧表中结点移动映射到新表
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hash(kot(cur->_data)) % newTables.size();
cur->_next = newTables[hashi];
newTables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newTables);
}
size_t hashi = hash(kot(data)) % _tables.size();
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
_size++;
return make_pair(iterator(newnode, this), true);
}
有了上面的插入,我们就可以在unordered_map中实现[]的操作了。
#pragma once
#include
#include
#include
using namespace std;
template
struct HashNode
{
T _data;
HashNode* _next;
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
};
//前置声明
template
class HashTable;
template
struct _HashIterator
{
typedef HashNode Node;
typedef HashTable HT;
typedef _HashIterator Self;
Node* _node;
HT* _ht;
_HashIterator(Node* node, HT* ht)
:_node(node)
, _ht(ht)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
Self& operator++()
{
if (_node->_next)
{
//当前桶迭代
_node = _node->_next;
}
else
{
// 找下一个桶
Hash hash;
KeyOfT kot;
size_t i = hash(kot(_node->_data)) % _ht->_tables.size();
i++;
for (; i < _ht->_tables.size(); i++)
{
if (_ht->_tables[i])
{
_node = _ht->_tables[i];
break;
}
}
//说明后面没有有数据的桶了
if (i == _ht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
bool operator!=(const Self& s)const
{
return _node != s._node;
}
bool operator==(const Self& s)const
{
return _node == s._node;
}
};
template
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
//特化
template<>
struct HashFunc
{
size_t operator()(const string& key)
{
size_t val = 0;
for (auto ch : key)
{
val *= 131;
val += ch;
}
return val;
}
};
template
class HashTable
{
typedef HashNode Node;
template
friend struct _HashIterator;
public:
typedef _HashIterator iterator;
iterator begin()
{
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i])
{
return iterator(_tables[i], this);
}
}
return end();
}
iterator end()
{
return iterator(nullptr, this);
}
~HashTable()
{
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
free(cur);
cur = next;
}
_tables[i] = nullptr;
}
}
pair insert(const T& data)
{
Hash hash;
KeyOfT kot;
//去重
iterator ret = Find(kot(data));
if (ret != end())
{
return make_pair(ret, false);
}
//扩容
if (_size == _tables.size())
{
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 10;
vector newTables;
newTables.resize(newsize, nullptr);
//将旧表中结点移动映射到新表
for (size_t i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hash(kot(cur->_data)) % newTables.size();
cur->_next = newTables[hashi];
newTables[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newTables);
}
size_t hashi = hash(kot(data)) % _tables.size();
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
_size++;
return make_pair(iterator(newnode, this), true);
}
iterator Find(const K& key)
{
if (_tables.size() == 0)
return end();
Hash hash;
KeyOfT kot;
size_t hashi = hash(key) % _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
return iterator(cur, this);
else
cur = cur->_next;
}
return end();
}
bool erase(const K& key)
{
if (_tables.size() == 0)
{
return false;
}
Hash hash;
KeyOfT kot;
size_t hashi = hash(key) % _tables.size();
Node* cur = _tables[hashi];
Node* prev = nullptr;
while (cur)
{
if (kot(cur->_data) == key)
{
// 1、头删
// 2、中间删
if (prev == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
_size--;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
vector _tables;
size_t _size = 0;
};
#pragma once
#include"HashTable.h"
namespace zdl
{
template>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename HashTable::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair insert(const K& key)
{
return _ht.insert(key);
}
private:
HashTable _ht;
};
#pragma once
#include"HashTable.h"
namespace zdl
{
template>
class unordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair& kv)
{
return kv.first;
}
};
public:
typedef typename HashTable, Hash, MapKeyOfT>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair insert(const pair& kv)
{
return _ht.insert(kv);
}
V& operator[](const K& key)
{
pair ret = _ht.insert(make_pair(key, V()));
return ret.first->second;
}
private:
HashTable, Hash, MapKeyOfT> _ht;
};
}