unordered_map&&unordered_set介绍
看名字是和map\set类似的迭代器,和map\set的区别如下:
- map底层是红黑树封装;unordered_map底层是哈希桶
- map支持双向迭代器;unordered_map只支持++
- map是有序的序列;unordered_map是无序的序列
- unordered_map的查找效率比map快
接口方面和map类似,参照map/set的使用即可。
unordered_set
哈希桶的封装部分
哈希桶的代码:哈希桶代码部分参考这里
为了更好适合封装map,在哈希桶里面修改内容如下:
- 添加了迭代器,提供了*,->,++,!=等功能。
- 哈希桶增加了begin和end函数
- 哈希桶insert的返回值修改(为了在map里实现[])
- 添加了const迭代器
具体实现出现的问题,在代码的注释中,有所体现:
namespace HashBucket
{
//T表示数据类型
template<class T>
struct Elem
{
Elem(const T& data)
:_data(data)
, _next(nullptr)
{}
T _data;
Elem<T>* _next;
};
template<class K>
struct Hash
{
size_t operator()(const K& key)
{
return key;
}
};
//特化版本
template<>
struct Hash<string>
{
//BKDR哈希*31
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto e : s)
{
hash += e;
hash *= 31;
}
return hash;
}
};
//迭代器实现
//迭代器用到了哈希表,需要前置声明一下(声明不需要写默认传参)
template<class K, class T, class KeyOFT, class HashFunc>
class HashTables;
// K索引, T数据类型, Ref:T的引用 , Ptr:T的指针, KeyOFT:获取T的索引
template<class K, class T, class Ref, class Ptr, class KeyOFT, class HashFunc = Hash<K>>
struct _HashIterator
{
typedef Elem<T> Node;
typedef HashTables<K, T, KeyOFT, HashFunc> HT;
typedef _HashIterator<K, T, Ref, Ptr, KeyOFT, HashFunc> Self;
typedef _HashIterator<K, T, T&, T*, KeyOFT, HashFunc> Iterator;
//const迭代器,不在这定义,在哈希表定义
//typedef _HashIterator<K, const T, const T&, const T*, KeyOFT, HashFunc> const_Iterator;
/*typedef _HashIterator<K, T, const T&, const T*, KeyOFT, HashFunc> const_Iterator;*/
Node* _node;
const HT* _ht; //迭代器还需要哈希表,因为要换桶,没有表无法完成换桶
//构造 begin要用,所以要public
public:
_HashIterator(Node* node, const HT* ht)
:_node(node)
,_ht(ht)
{}
//如果是普通迭代器,就是拷贝构造
//如果是const,就是构造函数
//_HashIterator(const Iterator& it)
// :_node(it._node)
// , _ht(it._ht)
//{}
public:
Ptr operator->()
{
return &(_node->_data);
}
//没有形参
//Ref operator->(const Node* node)
Ref operator*()
{
return _node->_data;
}
// !=号的迭代器注意一下
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
//next不为空,下一个结点就是next
if (_node->_next)
{
_node = _node->_next;
return *this;
}
KeyOFT kot;
HashFunc hash;
//首先确认桶号
size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
++hashi;
while (hashi<_ht->_tables.size()) //小于表长
{
if (_ht->_tables[hashi])
{
_node = _ht->_tables[hashi]; //找到了
break;
}
hashi++;
}
if (hashi == _ht->_tables.size()) //没找到非空的结点.说明是最后一个
{
_node = nullptr;
}
return *this;
}
};
//K表示查找值时的索引, T表示数据类型,KeyOFT获取T的索引K,HashFunc计算Hash的函数
template<class K, class T, class KeyOFT, class HashFunc = Hash<K>>
class HashTables
{
//迭代器要访问私有的_tables,得是友元
template<class K, class T, class Ref, class Ptr, class KeyOFT, class HashFunc>
friend struct _HashIterator;
typedef Elem<T> Node;
typedef T Data;
//迭代器的typedef,
//unordered_map的
public:
typedef _HashIterator<K, T, T&, T*, KeyOFT, HashFunc> iterator;
//const 迭代器
typedef _HashIterator<K, T, const T&, const T*, KeyOFT, HashFunc> const_iterator;
public:
//此版本不太好
//Iterator begin()
//{
// Node* cur = nullptr;
// for (auto e : _tables)
// {
// if (e)
// {
// return Iterator(e, this);
// }
// }
//}
iterator begin()
{
Node* cur = nullptr;
for (auto e : _tables)
{
if (e)
{
cur = e;
break;
}
}
return iterator(cur, this);
}
iterator end()
{
return iterator(nullptr, this);
}
const_iterator begin() const
{
Node* cur = nullptr;
for (auto e : _tables)
{
if (e)
{
cur = e;
break;
}
}
return const_iterator(cur, this);
}
const_iterator end() const
{
return const_iterator(nullptr, this);
}
pair<iterator, bool> insert(const Data& data)
{
//负载因子过大就扩容
CheckCapacity();
HashFunc hash;
KeyOFT kot;
iterator it = find(kot(data));
//if (it._node) //查到了,直接返回,不能访问迭代器的成员变量
if(it!=end())
{
/*return make_pair(iterator(cur,this), false);*/
return make_pair(it, false);
}
//散列地址
size_t hashi = hash(kot(data)) % _tables.size();
//直接插入头插
Node* newNode = new Node(data);
newNode->_next = _tables[hashi];
_tables[hashi] = newNode;
n++;
return make_pair(iterator(newNode, this), true);
}
iterator find(const K& key)
{
HashFunc 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);
}
cur = cur->_next;
}
return end();
}
bool erase(const K& key)
{
HashFunc hash;
KeyOFT kot;
size_t hashi = hash(key) % _tables.size();
Node* cur = _tables[hashi];
Node* prev = nullptr;
while (cur)
{
if (kot(cur->_data) == key)
{
if (!prev) //prev为空,说明是在表上
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
//扩容有问题,不应该插入一个就扩容,插入一个就扩容!!!!!!!!!!!!
void CheckCapacity()
{
KeyOFT kot;
HashFunc hash;
//表为空或者负载因子大于0.7了就扩容
//if (!_tables.size() || ((n * 10) / (_tables.size() * 10) > 7))
/*if (!_tables.size() || ((n * 10) / _tables.size() > 7))*/
if (n == _tables.size())
{
size_t newCapacity = (_tables.size() == 0 ? 10 : _tables.size() * 2);
HashTables* newHashTable = new HashTables;
newHashTable->_tables.resize(newCapacity);
//将旧表的数据重新映射到新表
//遍历旧表
for (auto& e : _tables)
{
if (e) //不空,就有数据
{
Node* cur = e;
Node* next; //下一个指针
while (cur)
{
size_t hashi = hash(kot(cur->_data)) % newCapacity;
next = cur->_next; //暂存下一个
cur->_next = newHashTable->_tables[hashi];
newHashTable->_tables[hashi] = cur; //头插
//newHashTable->n++;
cur = next;
}
}
}
//swap(*newHashTable, *this);
_tables.swap(newHashTable->_tables);
}
}
private:
vector<Node*> _tables;
size_t n = 0;
};
}
unordered_map的封装
- 迭代器类中只实现基本的操作不实现begin/end,哈希桶表提供begin/end,哈希桶里面通过typedef实现const迭代器的规则;
- map外面暴露了三个模板参数,可以自定义hash函数
- 支持[]取数据
- 注意成员变量定义方法
namespace xty
{
template<class K>
struct Hash
{
size_t operator()(const K& key)
{
return key;
}
};
//特化版本
template<>
struct Hash<string>
{
//BKDR哈希*31
size_t operator()(const string& s)
{
size_t hash = 0;
for (auto e : s)
{
hash += e;
hash *= 31;
}
return hash;
}
};
template<class K, class V,class HashFunc=Hash<K>>
class unordered_map
{
struct MyKeyOFT; //声明一下
//迭代器重命名
public:
typedef typename HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc>::iterator iterator;
typedef typename HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc>::const_iterator const_iterator;
private:
//只有自己知道自己的数据类型是什么样
struct MyKeyOFT
{
const K& operator()(const pair<K, V>& data)
{
return data.first;
}
};
public:
iterator begin()
{
return _hash.begin();
}
iterator end()
{
return _hash.end();
}
//要让map支持[],需要将insert的返回函数修改一下
V& operator[](const K& key)
{
pair<iterator, bool> ret = Insert(make_pair(key, V()));
return ret.first->second;
}
//插入
pair<iterator, bool> Insert(const pair<K, V>& data)
{
return _hash.insert(data);
}
//删除
bool Erase(const K& key)
{
return _hash.erase(key);
}
pair<K, V>* Find(const K& key)
{
return _hash.find(key);
}
private:
HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc> _hash;
//HashBucket::HashTables<K, V, MyKeyOFT> _hash; 注意要传pair ,不然insert报错
};
void unordered_map_test()
{
int a[]= { 11, 22, 33, 44, 55, 66, 77, 88, 99, 111, 444, 555,1111 };
unordered_map<int, int> map;
for (auto e : a)
{
map.Insert(make_pair(e, e));
}
}
}
添加一段测试代码检验第三个模板参数的正确性:
class Date
{
friend struct HashDate;
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d)const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
bool operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
friend ostream& operator<<(ostream& _cout, const Date& d);
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
struct HashDate
{
size_t operator()(const Date& d)
{
size_t hash = 0;
hash += d._year;
hash *= 31;
hash += d._month;
hash *= 31;
hash += d._day;
hash *= 31;
return hash;
}
};
// 一个类型要做unordered_map/unordered_set的key,要满足支持转换成取模的整形 和 ==比较
// 一个类型要做map/set的key,要满足支持<比较
/*if (cur->key < key)
{}
else if (key < cur->key)
{}
else
{}*/
void test_unordered_map4()
{
Date d1(2023, 3, 13);
Date d2(2023, 3, 13);
Date d3(2023, 3, 12);
Date d4(2023, 3, 11);
Date d5(2023, 3, 12);
Date d6(2023, 3, 13);
Date a[] = { d1, d2, d3, d4, d5, d6 };
unordered_map<Date, int, HashDate> countMap;
for (auto e : a)
{
countMap[e]++;
}
for (auto& kv : countMap)
{
cout << kv.first << ":" << kv.second << endl;
}
}
unordered_set封装
- 注意成员变量的参数不加const K
- typename
template<class K, class HashFunc=Hash<K>>
class unordered_set
{
struct SetOFT;
public:
//外面要使用迭代器,所以放public
typedef typename HashBucket::HashTables<K, K, SetOFT, HashFunc>::const_iterator iterator;
typedef typename HashBucket::HashTables<K, K, SetOFT, HashFunc>::const_iterator const_iterator;
public:
struct SetOFT
{
const K& operator()(const K& key)
{
return key;
}
};
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
const_iterator begin() const
{
return _ht.begin();
}
iterator end() const
{
return _ht.end();
}
pair<iterator, bool> insert(const K& key)
{
return _ht.insert(key);
}
iterator find(const K& key)
{
return _ht.find(key);
}
bool erase(const K& key)
{
return _ht.erase(key);
}
private:
//不加pair<K,K>
/*HashBucket::HashTables<K, pair<K, K>, SetOFT, HashFunc>*/
/*HashBucket::HashTables<K, const K, SetOFT, HashFunc> _ht;*/
HashBucket::HashTables<K, K, SetOFT, HashFunc> _ht;
};