😇 😇hello!我是bug。今天我们来讲讲unordered_map和unordered_set的相关内容:
(代码可能会有一点问题,请各位老铁指正 😘 😘 )
前言
🍉 🍉unordered_map和 unordered_set是一种专门用来进行搜索的容器或者数据结构,因为其底层是哈希表,所以其增删查改的时间复杂度都是O(1),其效率是很高的。注意:这里我们哈希表用的是拉链法,即数组里面存的是单链表的首元素指针,所以无法实现自减的重载,即哈希表不提供反向迭代器。
一、unordered_map
🌳 🌳区别:
🌱 unordered_map和map的功能类似,都是KV模型。
🌱 底层不同,map的底层是红黑树,unordered_map的底层是哈希表。
🌱 map有排序的功能,而unordered_map是无序的。
🌱 从效率上看,unordered_map增删查改的时间复杂度为O(1),而map增删查改的时间复杂度为O(logN)。
🌱 从空间上看,unordered_map消耗的空间比map空间更大。
🌱 使用场景:对数据有排序或者空间要求时,选择map;对效率有要求时,选择unordered_map。
🌵🌵 unordered_map的相关接口:
函数 | 用法 |
---|---|
operator= | 重载= |
begin | 返回第一个元素的iterator |
end | 返回最后一个元素下一个位置的iterator |
empty | 判空 |
size | 返回元素个数 |
operator[] | 通过重载[]进行插入、修改 |
insert | 插入键对值 |
erase | 删除元素 |
clear | 清除元素 |
find | 查找元素 |
bucket_size | 返回桶中元素的个数 |
bucket_count | 返回桶的个数 |
bucket | 返回当前元素所处的桶数 |
二、unordered_set
🌳 🌳unordered_set和set都是K模型。因为它们的底层不同,所以其本质还是哈希表和红黑树的区别,区别在上面介绍unordered_map的时候谈到,这里不用过多篇幅。
🌵🌵 unordered_set的相关接口:
函数 | 用法 |
---|---|
operator= | 重载= |
begin | 返回第一个元素的iterator |
end | 返回最后一个元素下一个位置的iterator |
empty | 判空 |
size | 返回元素个数 |
insert | 插入元素 |
erase | 删除元素 |
clear | 清除元素 |
find | 查找元素 |
bucket_size | 返回桶中元素的个数 |
bucket_count | 返回桶的个数 |
bucket | 返回当前元素所处的桶数 |
三、哈希表
🍒 🍒哈希表实现代码⬇️ ⬇️:
#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<assert.h>
using std::cin;
using std::cout;
using std::endl;
using std::pair;
using std::vector;
using std::string;
using std::make_pair;
//选出key
template<class K, class V>
struct PairSelect1st
{
const K& operator()(const pair<K, V>& kv) { return kv.first; }
};
template<class K>
struct KSelect1st
{
const K& operator()(const K& k) { return k; }
};
//转成整型
template<class K>
struct HashFunc
{
size_t operator()(const K& val) { return val; }
};
//模板的特化
template<>
struct HashFunc<std::string>
{
size_t operator()(const std::string& s1)
{
size_t sum = 0;
for (size_t i = 0; i < s1.size(); i++)
{
sum = sum * 131 + s1[i];
}
return sum;
}
};
//比较判断
template<class K>
struct equal_to
{
bool operator()(const K& lval, const K& rval) { return lval == rval; }
};
template<>
//模板特化
struct equal_to<string>
{
bool operator()(const string& s1, const string& s2) { return s1 == s2; }
};
//素数表
const int PRIMECOUNT = 28;
const size_t primeList[PRIMECOUNT] = {
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
namespace OpenHash
{
template<class T>
struct HashNode
{
typedef HashNode<T> Node;
typedef HashNode<T>* pNode;
HashNode<T>* _next;
T _data;
public:
HashNode(const T& data = T())
:_next(nullptr)
, _data(data)
{}
};
template<class K, class V, class T, class Pred, class Select1st, class HashFunc>
class HashTable;
template<class K, class V, class T, class Ref, class Ptr, class Pred, class Select1st, class HashFunc>
struct Iterator
{
typedef HashNode<T> Node;
typedef HashTable<K, V, T, Pred, Select1st, HashFunc> HashTable;
typedef Iterator<K, V, T, Ref, Ptr, Pred, Select1st, HashFunc> self;
Node* _pnode;
HashTable* _pHT;
Iterator(Node* pnode = nullptr, HashTable* pHT = nullptr) : _pnode(pnode), _pHT(pHT) {}
Ref operator*() { return _pnode->_data; }
const Ref operator*()const { return _pnode->_data; }
Ptr operator->() { return &_pnode->_data; }
const Ptr operator->()const { return &_pnode->_data; }
self& operator++()
{
if (_pnode == nullptr)
return *this;
if (_pnode->_next != nullptr)
{
_pnode = _pnode->_next;
return *this;
}
//_pnode->next == nullptr我们要去找现在的结点属于哪一个桶
size_t index = HashFunc()(Select1st()(_pnode->_data)) % _pHT->_table.size() + 1;
for (; index < _pHT->_table.size(); index++)
{
Node* cur = _pHT->_table[index];
if (cur != nullptr)
{
_pnode = cur;
return *this;
}
}
_pnode = nullptr;
return *this;
}
self operator++(int)
{
self tmp = *this;
++(*this);
return tmp;
}
bool operator!=(const self& it)const { return _pnode != it._pnode; }
bool operator==(const self& it)const { return _pnode == it._pnode; }
};
template
<class K, class V, class T, class Pred = equal_to<string>,
class Select1st = PairSelect1st<K, V>, class HashFunc = HashFunc<K>>
class HashTable
{
typedef HashNode<T>* pNode;
typedef HashNode<T> Node;
template<class K, class V, class T, class Ref, class Ptr, class Pred, class Select1st, class HashFunc>
friend struct Iterator;
private:
//存结点指针
vector<pNode> _table;
size_t _n;
public:
typedef Iterator<K, V, T, const T&, const T*, Pred, Select1st, HashFunc> const_iterator;
typedef Iterator<K, V, T, T&, T*, Pred, Select1st, HashFunc> iterator;
HashTable() :_n(0) {}
void clear()
{
for (size_t index = 0; index < _table.size(); index++)
{
pNode cur = _table[index];
pNode prev = cur;
while (cur)
{
prev = cur;
cur = cur->_next;
delete prev;
_table[index] = nullptr;
}
}
_n = 0;
}
~HashTable()
{
clear();
}
iterator begin()
{
size_t index = 0;
for (; index < _table.size(); index++)
{
pNode cur = _table[index];
if (cur != nullptr)
return iterator(cur, this);
}
return iterator(nullptr, this);
}
iterator end() { return iterator(nullptr, this); }
const_iterator cbegin()
{
size_t index = 0;
for (; index < _table.size(); index++)
{
pNode cur = _table[index];
if (cur != nullptr)
return const_iterator(cur, this);
}
return const_iterator(nullptr, this);
}
const_iterator cend() { return const_iterator(nullptr, this); }
pair<iterator, bool> insert(const T& data)
{
//如果为空,则开空间
if (!_table.size())
_table.resize(53ul);
//挑选key
Select1st st1;
//转换整型
HashFunc hf;
//判断是否冗余
iterator ret = find(data);
if (ret._pnode != nullptr)
return std::make_pair(iterator(ret._pnode, this), false);
//判断是否需要扩容
if ((double)_n / (double)_table.size() >= 1)
{
vector<pNode> new_table(GetNextPrime(_table.size()));
for (size_t i = 0; i < _table.size(); i++)
{
pNode cur = _table[i];
if (cur != nullptr)
{
pNode next = _table[i];
while (cur)
{
next = cur->_next;
size_t new_index = (hf(st1(cur->_data))) % new_table.size();
//头插
cur->_next = new_table[new_index];
new_table[new_index] = cur;
cur = next;
}
_table[i] = nullptr;
}
//不推荐,插入的时候重新创建结点,浪费
/*while(e != nullptr)
{
tmp.insert(e->_kv);
e = e->_next;
}*/
}
new_table.swap(_table);
}
//计算hashbucket的下标
size_t index = hf(st1(data)) % _table.size();
pNode newNode = new Node(data);
//头插
newNode->_next = _table[index];
_table[index] = newNode;
_n++;
return std::make_pair(iterator(newNode, this), true);
}
iterator find(const T& data)
{
HashFunc hf;
Select1st slt;
if (_table.size() == 0)
return iterator(nullptr, this);
size_t index = hf(slt(data)) % _table.size();
pNode cur = _table[index];
while (cur)
{
if (Pred()(slt(cur->_data), slt(data)))
return iterator(cur, this);
else
cur = cur->_next;
}
return iterator(nullptr, this);
}
bool erase(const T& data)
{
Select1st st1;
size_t index = HashFunc()(st1(data)) % _table.size();
pNode cur = _table[index];
pNode prev = cur;
while (cur)
{
if (Pred()(st1(cur->_data), st1(data)))
{
//找到了
if (cur == _table[index])//头结点
{
_table[index] = nullptr;
_n--;
delete cur;
return true;
}
else
{
prev->_next = cur->_next;
_n--;
delete cur;
return true;
}
}
else//没找到
{
prev = cur;
cur = cur->_next;
}
}
return false;
}
size_t GetNextPrime(size_t prime)const
{
size_t i = 0;
for (; i < PRIMECOUNT; i++)
{
if (primeList[i] > primeList[prime])
return primeList[i];
}
return primeList[i];
}
size_t size() const { return _n; }
bool empty()const { return _n == 0; }
//返回桶的个数,即_table的大小
size_t bucket_count()const { return _table.size(); }
//返回桶里面的元素个数
size_t bucket_size(size_t n)const
{
assert(n < _table.size());
size_t count = 0;
pNode cur = _table[n];
while (cur)
{
cur = cur->_next;
count++;
}
return count;
}
//返回当前key所在的桶数
size_t bucket(const T& data)const
{
size_t index = HashFunc()(Select1st()(data)) % _table.size();
pNode cur = _table[index];
while (cur)
{
if (Pred()(Select1st()(cur->_data), Select1st()(data)))
return index;
else
if (cur != nullptr)
cur = cur->_next;
}
return -1;
}
};
}
😜 😜 哈希表之前我们实现过,这里就不进行详细介绍了。
四、unordered_map的模拟实现
🍒 🍒unordered_map模拟实现代码⬇️ ⬇️:
#pragma once
#include"HashTable.h"
namespace lz
{
template<class K,class V,class Hash = HashFunc<K>,class equal = equal_to<K>>
class Unordered_Map
{
private:
OpenHash::HashTable<K, V, pair<K, V>,equal,PairSelect1st<K, V>, Hash> _ht;
typedef Unordered_Map<K, V, Hash, equal> self;
public:
typedef OpenHash::Iterator<K, V, pair<K, V>, pair<K, V>&, pair<K, V>*,
equal, PairSelect1st<K, V>, HashFunc<K>> iterator;
typedef OpenHash::Iterator<K, V, pair<K, V>, const pair<K, V>&, const pair<K, V>*,
equal, PairSelect1st<K, V>, HashFunc<K>> const_iterator;
Unordered_Map() :_ht() {}
Unordered_Map(const self& s) { _ht = s._ht; }
//迭代器
iterator begin() { return _ht.begin(); }
iterator end() { return _ht.end(); }
const_iterator cbegin()const { return _ht.cbegin()._pnode->_data; }
const_iterator cend()const { return _ht.cend()._pnode->_data; }
//插入
pair<iterator, bool> insert(const pair<K, V>& kv) { return _ht.insert(kv); }
//删除
bool erase(const iterator& it) { return _ht.erase(it._pnode->_data); }
//查找
iterator find(const pair<K, V>& kv) { return _ht.find(kv); }
//元素个数
size_t size()const { return _ht.size(); }
//判空
bool empty()const { return _ht.empty(); }
//重载=
self& operator=(const self& s) { _ht = s._ht; return *this; }
//重载[]
V& operator[](const K& key) { return insert(make_pair(key, V())).first._pnode->_data.second; }
//清除
void clear() { _ht.clear(); }
//返回桶的个数
size_t bucket_count()const { return _ht.bucket_count(); }
//返回桶里面的元素
size_t bucket_size(size_t n)const { return _ht.bucket_size(n); }
//返回当前key所在的桶数
size_t bucket(const pair<K, V>& kv)const{ return _ht.bucket(kv); }
};
}
五、unordered_set的模拟实现
🍒 🍒unordered_set模拟实现代码⬇️ ⬇️:
#pragma once
#include"HashTable.h"
namespace lz
{
template<class K,class Hash = HashFunc<K>, class equal = equal_to<K>>
class Unordered_Set
{
private:
OpenHash::HashTable<K, K, K, equal, KSelect1st<K>, Hash> _ht;
public:
typedef OpenHash::Iterator<K, K, K, K&, K*,equal, KSelect1st<K>, Hash> iterator;
typedef OpenHash::Iterator<K, K, K, const K&, const K*,equal, KSelect1st<K>, Hash> const_iterator;
iterator begin() { return _ht.begin(); }
iterator end() { return _ht.end(); }
const_iterator cbegin() { return _ht.cbegin(); }
const_iterator cend() { return _ht.cend(); }
//插入
pair<iterator, bool> insert(const K& key) { return _ht.insert(key); }
//删除
bool erase(const iterator& it) { return _ht.erase(it._pnode->_data); }
//查找
iterator find(const K& key) { return _ht.find(key); }
//元素个数
size_t size()const { return _ht.size(); }
//清除
void clear() { _ht.clear(); }
//返回桶的个数
size_t bucket_count()const { return _ht.bucket_count(); }
//返回桶里面的元素
size_t bucket_size(size_t n)const { return _ht.bucket_size(n); }
//返回当前key所在的桶数
size_t bucket(const K& key)const { return _ht.bucket(key); }
};
}
六、测试代码
🍒 🍒测试代码⬇️ ⬇️:
#define _CRT_SECURE_NO_WARNING
#include"HashTable.h"
#include"Unordered_Map.h"
#include"Unordered_Set.h"
#include"BitSet.h"
#include"BloomFilter.h"
void Test_Unordered_Map1()
{
lz::Unordered_Map<string, string> um1;
pair<string, string> arr[] = {
make_pair("left", "左边") ,make_pair("right", "右边"),make_pair("up", "向上")
,make_pair("down", "向下"),make_pair("left","左边"),make_pair("eat","吃")
,make_pair("sleep","睡觉"),make_pair("run","跑"),make_pair("jump","跳") };
//测试判空
cout << um1.empty() << endl;
for (const auto& e : arr)
um1.insert(e);
//测试删除
for (const auto& e : um1)
cout << e.first << ":" << e.second << endl;
um1.clear();
for (const auto& e : um1)
cout << e.first << ":" << e.second << endl;
//通过[]插入
um1["abc"] = "xx";
for (auto str : um1)
cout << str.first << ":" << str.second << endl;
//修改
um1["abc"] = "my";
for (const auto& str : um1)
cout << str.first << ":" << str.second << endl;
cout << endl;
//用引用,防止深拷贝
for (const auto& e : arr)
um1.insert(e);
lz::Unordered_Map<string, string>::iterator it = um1.begin();
while (it != um1.end())
{
um1.erase(it);
it = um1.begin();
}
cout << endl;
//测试bucket_count、bucket_size、bucket
cout << um1.bucket_count() << endl;
cout << um1.bucket(make_pair("left","左边")) << endl;//找不到就返回-1
//cout << um1.bucket_size(um1.bucket(pair<string,string>("left","左边"))) << endl;
}
void Test_Unordered_Map2()
{
lz::Unordered_Map<string, int> um1;
string arr[] = { "苹果","梨","苹果","梨","梨","梨","苹果","香蕉","香蕉", "香蕉" ,"西瓜" };
for (const auto& str : arr)
{
if (um1.find(make_pair(str, 1))._pnode == nullptr)
um1.insert(make_pair(str, 1));
else
um1[str]++;
}
for (const auto& str : um1)
cout << str.first << ":" << str.second << endl;
}
void Test_Unordered_Set()
{
lz::Unordered_Set<string> us1;
string arr[] = {
"left", "左边" ,"right", "右边","up", "向上"
,"down", "向下","left","左边","eat","吃"
,"sleep","睡觉","run","跑","jump","跳" };
//插入
for (const auto& e : arr)
us1.insert(e);
for (const auto& str : us1)
cout << str << " ";
//删除
lz::Unordered_Set<string>::iterator it = us1.begin();
while (it != us1.end())
{
us1.erase(it);
it = us1.begin();
}
cout << endl;
//测试bucket_count、bucket_size、bucket
cout << us1.bucket_count() << endl;
cout << us1.bucket("left") << endl;
//cout << us1.bucket_size(us1.bucket("left")) << endl;
}
void Test_BitSet()
{
lz::BitSet bs1(200);
bs1.set(100);
bs1.set(100);
bs1.set(0);
//bs1.reset(100);
//bs1.reset(0);
cout << bs1.test(100) << endl;
//cout << bs1.test(50) << endl;
cout << bs1.size() << endl;
cout << bs1.count() << endl;
}
void Test_BloomFilter()
{
lz::BloomFilter<string> bf(18);
string arr[] = {
"left", "左边" ,"right", "右边","up", "向上"
,"down", "向下","left","左边","eat","吃"
,"sleep","睡觉","run","跑","jump","跳" };
//插入
for (const auto& str : arr)
bf.insert(str);
//判断
for (const auto& str : arr)
cout << bf.IsInBloomFilter(str) << " ";
cout << bf.IsInBloomFilter("abc");
}
int main()
{
//Test_BloomFilter();
//Test_BitSet();
//Test_Unordered_Map1();
//Test_Unordered_Map2();
//Test_Unordered_Set();
return 0;
}
🍋 🍋 测试中包括了unordered_map的功能测试,unordered_map字典和计数的实现,以及unordered_set的功能测试。
😎 😎今天的内容到这里就结束了,希望各位小伙伴们能够有所收获,我们下期再见!