赞
踩
目录
什么是哈希表?
哈希表,顾名思义,就是一个表。
可是为什么叫哈希表?
因为这是从老美哪里音译过来的
叫做->Hash Table
翻译过来就是->哈希表
既然是表,那么
第一,这个哈希表长什么样子?
第二,为什么会有这个哈希表?
第三,这个哈希表用来做什么?
第三,这个哈希表的特点是什么?
第四,什么是取余法?
第五,什么是映射?
第六,什么是线性探测?
第七,什么是哈希桶?
一些常见的概念,是什么?要怎么理解?
下面一一我来解析。
一般哈希表有两种形式(先别问为什么,先看,后面解释)
假设你有一个数组或者链表,
传统的数组访问某一个数据,或者链表访问某一个数据,必须遍历,也只有遍历。
假如你的数组长度为100万,你现在要取某个值,而你知道这个值在就在数组的中间。
怎么办?
此时,无论从前往后,还是从后往前遍历,都绕不过50万个值。
蛋不蛋疼?蛋疼。
难受不难受?难受。
如果数据规模更大,例如100亿,那更难受。
所以,有困难,有麻烦,就会引发思考:我能不能不用遍历,咔的一下,马上就找到这个值?
而传统的数据结构显然无法突破这个难题。
既然旧的不行,干脆,那就搞一个新的。
于是,天空一声巨响,哈希表闪亮登场!
所以,哈希表就是为了解决查找必须遍历的问题而生。
如何做到不遍历直接访问到数据?
很简单,非常简单,简单到不能再简单。
举个例子,你有5个值:1, 2, 3, 4 ,10001
我在创建数组的时候,我直接申请10001个空间!
然后所有的数据直接存储在对应的下标位置。
(啊???)
现在你给我一个值,例如说10001。
要我去表里找,此时我不去遍历。
我直接就到数组下标为10001的位置取数据。
因为数组是支持随机访问的。
我直接初始位置+10001,直接就拿到数据了。
有毛病没有?没有。
有问题没有?没有。
你会说,是不是太浪费空间了?
是的,非常浪费。
但是,你就说我遍历了没有?没有。
你就说找到了没有?找到了。
你就说快不快?非常快,O(1)。
所以,解决我们原来要遍历的困扰没有?解决了。
但是,你会发现不对劲,空间太浪费了啊。
假设我有只有2个数据,一个是1,另一个是1000000000000000。
(嗯......?!)
难道我要建立那么大的空间,就为了存这俩货?
不行,很不行,非常不行。
那么,怎么办?
于是,取余法就来了。
现在,我们回到文章开始的地方,哈希表的模样,其中一种是这样的:
为什么是这样的?
下面我们来解释:
假如,我们有以上数据:2,18,22,89,1000000000001
按照原来的办法,我们需要开辟10000000000001个空间,才能构成哈希表
即数据存储在对应位置
但是,这种方式不行,太浪费空间。
怎么办?取余法。
什么是取余法?
很简单,就是取余数。
例如,我们现在有5个数据,那么我们就开10个空间
然后,让这每一个数据%10,得到结果
再将这个结果放到对应的位置。
什么意思?
很简单,现在有6个数据:2,18,22,23,89,1000000000001
2%10 = 2,所以2存放在下标为2的位置
18%10 = 8,所以2存放在下标为8的位置
22%10 = 2,所以存放在下标为2的位置
但是,现在下标为2的位置已经有了一个值
而这个,就是所谓的哈希冲突!非常简单,非常好理解对不对?
是的,就是这么简单。不要被一些看似很牛b的概念给困住了。
所有的概念都只不过是为了更好的概括和综合某个现象,方便于你理解,仅此而已。
学习新事物,也是如此。
好的,那么,这个位置冲突了,怎么办?
那就放到后面没有冲突的位置。
下标为3的位置没有冲突,所以,放到3
23%10 = 3,所以23存放在下标为3的位置
但是,同样的,下标为3的位置已经有数据了。
即所谓哈希冲突了。
怎么办?
很简单,同样的道理,往后挪
挪到没有冲突的位置。
所以,往后下标4的位置没有数据,所以23存在下标为4的位置
89%10 = 9,所以89存放在下标为9的位置
1000000000001%10 = 1,所以1000000000001存放在下标为1的位置
于是,这6个数据,即使有数据为1000000000001那么大的值,
我们也仅仅用了10个空间就存储下来了。
那么,如何取出数据呢?
同样的,很简单,例如取18,将18%10=8,然后到下标为8的位置取即可。
但是,22呢?22%10=2,但是22却不在下标为2的位置。
怎么办呢?
往后找。
23呢?也不在下标为3的位置。
怎么办呢?
往后找。
如果当前位置找不到值,就往后挨个查找,直到找到。
往后找,就像是探测,而且是一个一个探测,是线性的查找。
这就是所谓线性探测!
所以,我们把这个表,就叫做线性探测表
非常简单。
同时,你会发现:
22并不是存在下标为22的位置,而是存在下标为2的位置
1000000001也不是存在下标为1000000001的位置,而是存在下标为1的位置
这就是所谓映射!
即,值与值之间的关联,一种关系。
22和下标为2的位置的映射关系。
1000000001和下标为1的位置的映射关系。
还可以这么理解:
你名字叫做张三,你的发小叫做李四。
别人叫张三,叫的是你,而不是你发小。
这就是映射,张三映射你,李四映射你发小。
这就是一对一映射。
同时,还会有这一种情况:
你遇到了一个人,她名字也叫做张三!
现在,有人叫张三的名字,但是张三有两个人。
所以,张三映射对应两个人。
甚至,张三有很多很多,例如说张伟这个名字。
这就是一对多的映射。
取余法,解决数据存放空间太大的问题
好了,现在我们已经解决了空间太大的问题。
但是,问题又来了。
什么问题?
例如,假设我们的数据老是冲突,怎么办?
这个时候的访问,就会偏离我们初始的目的,即不遍历
因为访问一个数据,老是要往后遍历,很麻烦
随着数据的增多,冲突的概率增加,查找的成本越来越高。
也就是说,问题源于数据太多,而空间不够
怎么办?
很简单,扩容。
那么,我们应该什么时候扩容呢?
很简单,用负载因子判断。
好,什么是负载因子?
负载因子就是数据个数所占整个空间的比率。
例如
10个空间,有2个数据
负载因子就是0.2
10个空间,有7个数据
负载因子就是0.7
所以,每插入一个位置,我们就让负载因子+1
而一般来说,负载因子达到0.7就要进行扩容。
回到文章开始,我们说哈希表一般有两种形式,
一个叫做线性探测表,前面已经解释清楚。
另一个,叫做哈希桶。
长这个样子:
我们已经知道哈希桶长什么样子,下面我们来解释:
为什么要有哈希桶?
假如我有一组数据。
这一组数据是:2,22,222,2222,22222,222222,2222222,22222222........
好的,数据我给你了。
你存吧。
你想要怎么存?
如果按照线性探测表的方式进行存储:
好家伙,你一看数据,你发现
取余数,结果全是2
存一个冲突一个
存两个冲突两个
存三个冲突三个
.....
从头冲突到尾,没完没了。
怎么办?
很明显,线性探测形式的哈希表有着致命的弱点
即无法对余数相同的数据进行处理
冲突了,只有往后放
我的位置冲突了,我就去放别人的位置,让别人冲突
(没错,就是这么强大,你打我啊)
如此一来,当数据越来越多时,哈希冲突的概率将会越来越大
哈希冲突多了,就会导致查找的成本越来越高
哈希表的优势也会越来越微弱
怎么办?
于是,哈希桶来了。
我这么存:
我现在无论是存12,22,32,42,52还是222222222222
还会和其他位置冲突吗?
不会。
哈希桶将冲突的值,放到同一个位置下,用单链表管理起来
哈希桶的结构,极大的优化了线性探测表无法处理哈希冲突的缺点
同时,单链表的访问,其时间复杂度也控制在O(n)的量级。
非常棒。
哈希桶也存在负载因子的问题,
和线性探测负载因子是一样的逻辑
同样,也是大于0.7就进行扩容
扩容是对数组的扩容
而扩容之后,单链表的长度也会变短
为什么?
例如空间从10变为100
原来2,12,22,32,42,52,62,72,82,92等值都只能挂在下标2的位置
但是当空间变为100
瞬间,所有的值都有了自己对应的下标位置
原本长度为10的哈希桶,直接变为1
优化效果相当棒
这,就是哈希桶
1、闲散列:开放定址法(线性探测/二次探测)
二次检测:当数据比较集中的时候,查找会比较慢,
为了更快的查找,下一个位置查找偏移量不为1,可以为2次方
思路:我的位置被别人占了,我就去占别人得位置
冲突越多,效率越低
因此,有人提出了开散列
2、开散列:哈希桶/拉链法
综上,我们来总结一下:
1、值很分散,因此哈希表也叫做散列表
2、有些值不好映射,比如string,结构体等
3、开空间问题,即哈希冲突
4、哈希表是用哈希思想实现的一种数据结构
hash更多的来说,是一种思想
例如编码表也是一种哈希编码的运用
例如经典的ASCII码表
同时,有时候你会发现你打开某些文件,会出现乱码
为什么?
因为码表对错了,原本这个文件要拿表A来映射
结果拿成表B来映射了
所以结果就是乱码
乱码的本质是编码表对乱了
以上就是关于哈希表的基础概念和知识。
下面博主要带大家设计出一个简易版哈希表
即unordered_set和unordered_map
使用c++实现,总体还是比较难的
涉及模板、多层嵌套封装、泛型编程、内外部类、友元等
需要有一定的c++基础。
其实简单理解就够了,不必跟着我写出一个。
如果位置不被占,插入;如果位置被占,遇到空才插入
插入逻辑:
先是数据%size,为什么是size而不是capacity呢?
因为capacity后面的的值是没法访问的,end位置是size前一个位置
然后找到空/被删除的位置,插入,n++
n是记录哈希表个数的值,为什么不用size呢?
因为hash表是离散的表
如果hash表数值很多,就有很大的概论发生冲突
怎么办?
设置一个负载因子,用来记录hash表内的数据个数的占比
一般是0.7~0.8
如果hash表满了呢?在找空/被删除位置时,就会出现死循环的问题
满了就扩容
扩容之后,原有的值不能拷贝,需要重新映射,新的hash表的size是原有空间的2倍
处理数据冗余:插入前利用find
遇到空才停止,但是如果中间位置有空位置,就查不到后面的位置,如何解决?
设置一个位置状态:EMPTY、DELETE,EXIST
删除的值不必抹除,而是将状态设置为DELETE。
如果抹除,设置为什么值都不合适
因此,删除是一个伪删除,查找值时依旧能找到
在Find函数需要特殊判断,即存在才查找
如果数据不能取%怎么办?
例如string数据类型
用仿函数进行解决
对于整型、浮点型、指针都进行size_t强转为整型,就可以取%
但是string不可以转化为整型
怎么办?
单独写一个为string转换的仿函数
这个仿函数返回string【0】
把字符串转型为整型
将每一个字符的ASCII值*某个数值,累计加
最后每一个字符串的结果一般都不会重复,但是依旧会重复
string很常见,那可以对仿函数使用特化
什么是特化?就是对某些类进行特殊化处理
就是不适用模板推广,而是直接使用
一个哈希表要有的功能:
插入、删除、遍历(迭代器)
插入、删除、查找的接口封装
1)首先是一个迭代器对象,完成对象的简单框架
哈希表的迭代器,需要哈希表本身,以及哈希表对应位置的节点
其内部的哈希表,需要外部调用对象哈希表来初始化
2)++重载
1、先算出当前所在位置
2、当前位置的下一个位置不为空,那就是有直接返回
3、当前位置为空,要继续找后面的位置
返回值是一个pair,第一个参数是迭代器,第二个参数是bool,判断是否添加成功
如果已经存在,返回已存在节点的迭代器,以及false
如果节点不存在,返回新插入节点的迭代器,以及true
需要修改insert的返回值
以及find的返回值
这样就可以直接对find和insert进行复用
方括号重载返回值为value
- #pragma once
- #include<iostream>
- #include<vector>
- using namespace std;
-
- //设计一个hash表
- //哈希表是一个vector数组
- //节点存储的是一个pair节点
- //使用模板编程
-
- //如果插入的是string,就不能取%
- //怎么办?字符串哈希算法
- //使用仿函数
-
- enum State
- {
- EXIST,
- DELETE,
- EMPTY
- };
-
- template<class K>
- struct hashFunc
- {
- size_t operator()(const K& key)
- {
- return (size_t)key;
- }
- };
-
- template<>
- struct hashFunc<string>
- {
- size_t operator()(const string& key)
- {
- size_t ret = 0;
- for (auto ch : key)
- {
- ret *= 131;
- ret += ch;
- }
- return ret;
- }
- };
-
-
- namespace hash
- {
- //哈希表节点
- template<class K, class V>
- struct HashData
- {
- pair<K, V> _kv;
- State _state = EMPTY;
- };
-
- //定义哈希表
- template<class K, class V, class Hash = hashFunc<K>>
- class HashTable
- {
- public:
- typedef HashData<K, V> Node;
- HashTable()
- {
- _tables.resize(10);
- }
-
- //插入
- bool Insert(const pair<K, V>& kv)
- {
- if (Find(kv.first))
- {
- return false;
- }
-
- //检查扩容
- if (_n / _tables.size() >= 0.7)
- {
- size_t newSize = _tables.size() * 2;
- HashTable<K,V,Hash> newHashTable;
- newHashTable._tables.resize(newSize);
-
- //重新插入新空间
- for (size_t i = 0; i < _tables.size(); ++i)
- {
- newHashTable.Insert(_tables[i]._kv);
- }
- }
-
- //1、找到插入的位置,取%
- Hash hs;
-
- size_t hashi = hs(kv.first) % _tables.size();
- while (_tables[hashi]._state != EMPTY)
- {
- ++hashi;
- }
-
- //3、插入,更新负载因子
- _tables[hashi]._kv = kv;
- _tables[hashi]._state = EXIST;
- _n++;
-
- return true;
- }
-
- //查找
- Node* Find(const K& key)
- {
- Hash hs;
- size_t hashi = hs(key) % _tables.size();
- while (_tables[hashi]._state != EMPTY)
- {
- if (_tables[hashi]._state == EXIST &&
- _tables[hashi]._kv.first == key)
- {
- return &_tables[hashi];
- }
-
- ++hashi;
- hashi %= _tables.size();
- }
-
- return nullptr;
- }
-
- size_t size()
- {
- return _n;
- }
-
- //删除
- bool Erase(const K& key)
- {
- Node* cur = Find(key);
- if (cur)
- {
- cur->_state = DELETE;
- --_n;
- return true;
- }
-
- return false;
- }
-
- private:
- vector<HashData<K, V>> _tables;
- size_t _n = 0;
-
- };
-
- void HashTest()
- {
- HashTable<int, int> ht;
- ht.Insert({ 1,1 });
- ht.Insert({ 2,2 });
- ht.Insert({ 3,3 });
- ht.Insert({ 4,4 });
- ht.Insert({ 3,3 });
- ht.Insert({ 3,3 });
- cout << ht.Find(1) << endl;
- cout << ht.Find(2) << endl;
- cout << ht.Find(3) << endl;
- cout << ht.Find(4) << endl;
- cout << ht.size() << endl;
-
- ht.Erase(1);
- ht.Erase(2);
- cout << ht.size() << endl;
-
- }
-
- void HashTest1()
- {
- HashTable<string, int> ht;
- ht.Insert({ "abcd",1});
- ht.Insert({ "edasdfas",2});
- ht.Insert({ "kahkahdk",3});
- ht.Insert({ "ohjahsflhasf",4});
-
- cout << ht.size() << endl;
- size_t ret = hashFunc<string>()("dasldfhalf");
- size_t ret1 = hashFunc<string>()("sad");
- cout << ret << endl;
- cout << ret1 << endl;
-
- }
- }
-
-
-
-
-
-
-
- //哈希桶实现
- namespace hash_bucket
- {
-
- //哈希节点,是一个链表
- template<class T>
- struct HashNode
- {
- T _data;
- HashNode* _next;
-
- HashNode(const T& data)
- :_data(data)
- , _next(nullptr)
- {
- }
-
- };
-
- 前置声明
- //template<class K, class T, class Hash, class KeyOfT>
- //class HashTable;
-
- 实现迭代器
- //template<class K, class T, class Hash, class KeyOfT>
- //struct __HSIterator//这个是给hash表底层使用的迭代器对象
- //{
- // typedef HashNode<T> Node;
- // typedef HashTable<K, T, Hash, KeyOfT> Self;
-
- // HashTable<K, T, Hash, KeyOfT>* _pht;
- // Node* _node;
-
- // __HSIterator(const Node* node, const HashTable<K, T, Hash, KeyOfT>* pht)
- // :_pht(pht)
- // ,_node(node)
- // {
- // }
-
- // //++
- // Self& operator++()//返回结构体对象
- // {
- // KeyOfT kot;
- // Hash hs;
- // size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
- // Node* cur = _pht->_tables[hashi];
- //
- // if (_node->_next)
- // {
- // _node = _node->_next;
- // }
- // else//该节点为空
- // {
- // while (hashi < _pht->_tables.size())
- // {
- // if (cur == nullptr)
- // {
- // ++hashi;
- // }
- // else
- // {
- // break;
- // }
- // }
-
- // if (hashi == _pht->_tables.size())
- // {
- // _node = nullptr;
- // }
- // else
- // {
- // _node = _pht->_tables[hashi];
- // }
- // }
- //
- // return *this;
-
- // }
-
- // //解引用*
- // T& operator*()
- // {
- // return _node->_data;
- // }
-
- // T* operator->()
- // {
- // return &_node->_data;
- // }
-
- // bool operator!=(const Self& s)
- // {
- // return _node != s._node;
- // }
-
- //};
-
- //哈希表
- template<class K, class T, class Hash , class KeyOfT>
- class HashTable
- {
- public:
- 友元声明(但是这种方式的代码过于冗余)
- //template<class K, class T, class Hash, class KeyOfT>
- //friend struct __HSIterator;
-
-
- //内部类
- template<class Ref, class Ptr>
- struct __HSIterator//这个是给hash表底层使用的迭代器对象
- {
- typedef HashNode<T> Node;
- typedef __HSIterator Self;
-
- const HashTable* _pht;
- Node* _node;
-
- __HSIterator( Node* node, const HashTable* pht)
- :_pht(pht)
- , _node(node)
- {
- }
-
- //++
- Self& operator++()//返回结构体对象
- {
-
-
- if (_node->_next)
- {
- _node = _node->_next;
- }
- else//该节点为空
- {
- KeyOfT kot;
- Hash hs;
- size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
-
- ++hashi;
- while (hashi < _pht->_tables.size())
- {
- if (_pht->_tables[hashi])
- break;
- ++hashi;
-
- }
-
- if (hashi == _pht->_tables.size())
- {
- _node = nullptr;
- }
- else
- {
- _node = _pht->_tables[hashi];
- }
-
- }
-
- return *this;
-
- }
-
- //解引用*
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr operator->()
- {
- return &_node->_data;
- }
-
- bool operator!=(const Self& s)
- {
- return _node != s._node;
- }
-
- };
-
- typedef __HSIterator<T&, T*> iterator;
-
- iterator begin()
- {
- //找到第一个非空节点
- for (int i = 1;i< _tables.size(); ++i)
- {
- if (_tables[i])
- {
- return iterator(_tables[i], this);
- }
-
- }
- return end();
- }
-
- iterator end()
- {
- //直接是空
- return iterator(nullptr, this);
- }
-
-
- typedef HashNode<T> Node;
- public:
-
- HashTable()
- {
- _tables.resize(10,nullptr);
- }
-
- //插入
- pair<iterator, bool> Insert(const T& data)
- {
- Hash hs;//取模仿函数
- KeyOfT kot;//取key仿函数
- iterator it = Find(kot(data));
-
- if (it._node != nullptr)//存在节点
- {
- return make_pair(it, false);
- }
-
- //扩容
- if (_n == _tables.size())
- {
-
- vector<Node*> newTable(_tables.size() *2,nullptr);
-
- for (size_t i = 0; i<_tables.size(); ++i)
- {
- //首先,插入头节点
- Node* cur = _tables[i];
- //再处理后面的串
- while (cur)
- {
- Node* next = cur->_next;
- size_t hashi = hs(kot(cur->_data)) % newTable.size();
- //头插
- cur->_next = newTable[hashi];
- newTable[hashi] = cur;
- cur = next;
- }
-
-
- }
- _tables.swap(newTable);
-
- }
-
- size_t hashi = hs(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)
- {
- Hash hs;
- KeyOfT kot;
- size_t hashi = hs(key) % _tables.size();
- Node* cur = _tables[hashi];
- while (cur)
- {
- if (kot(cur->_data) == key)
- {
- return iterator(cur, this);
- }
-
- cur = cur->_next;
- }
- return iterator(nullptr, this);
- }
-
- size_t size()
- {
- return _n;
- }
-
- //删除
- bool Erase(const K& key)
- {
- KeyOfT kot;
- KeyOfT hs;
- Node* prev = nullptr;
- size_t hashi = hs(key) % _tables.size();
- Node* cur = _tables[hashi];
-
- while (cur)
- {
- if (kot(cur->_data) == key)
- {
- //如果是第一个节点
- if (prev == nullptr)
- {
- _tables[hashi] = cur->_next;
- }
- else
- {
- prev->_next = cur->_next;
- }
- delete cur;
-
- return true;
- }
- else
- {
- prev = cur;
- cur = cur->_next;
- }
- }
- return false;
- }
-
- private:
- vector<Node*> _tables;
- size_t _n = 0;
- };
-
- //void test_hash_bucket1()
- //{
- // HashTable<string, int> hb;
- // hb.Insert({"asada",1});
- // hb.Insert({"DASDAS",1});
- // hb.Insert({"DASDAS",1});
- // hb.Insert({"DASDAS",1});
- // hb.Insert({"DASDAS",1});
- // hb.Insert({"DASDAS",1});
- // hb.Insert({"DASbAS",1});
- // hb.Insert({"HHDH",1});
- // //cout << hb.Erase("") << endl;
- // cout << hb.size() << endl;
- // cout << hb.Find("asada") << endl;
- // cout << hb.Find("DASDAS") << endl;
- // cout << hb.Find("HHDH") << endl;
- //
- //}
-
- //void test_hash_bucket2()
- //{
- // vector<int> v = {1,2,3,4,5,65,6,7,8,9,90,0,2,3,23,45,232};
- // HashTable<int, int> hb;
- // for (size_t i = 0; i<v.size(); ++i)
- // {
- // if (i == 9)
- // {
- // int j = 0;
- // }
- // hb.Insert(make_pair(v[i],v[i]));
- // }
- // cout << hb.size() << endl;
- //}
-
-
- }
- #pragma once
- #pragma once
- #include"HashTable.h"
-
- namespace my_unordered_map {
-
-
- template<class K, class V, class Hash = hashFunc<K> >
- class unordered_map
- {
- public:
- //仿函数d
- struct MapKeyOfT
- {
- const K& operator()(const pair<K, V>& kv)
- {
- return kv.first;
- }
- };
-
-
- typedef typename hash_bucket::HashTable<K, pair<const K, V>, Hash, MapKeyOfT>::iterator iterator;
-
- iterator begin()
- {
- return _ht.begin();
- }
-
- iterator end()
- {
- return _ht.end();
- }
-
- pair<iterator, bool> insert(const pair<K, V>& kv)
- {
- return _ht.Insert(kv);
- }
-
- iterator find(const K& key)
- {
- return _ht.Find(key);
- }
-
- bool erase(const K& key)
- {
- return _ht.Erase(key);
- }
-
- //方括号重载
- V& operator[](const K& key)
- {
- pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));//V()为匿名对象
- return ret.first->second;
- }
-
- private:
- hash_bucket::HashTable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;
- };
-
- void test_unordered_map()
- {
- unordered_map<int,int> um;
- um.insert({1,1});
- um.insert({2,1});
- um.insert({3,1});
- um.insert({4,1});
- um.insert({5,1});
- um.insert({6,1});
- um.find(1);
- //cout << um.find(2) << endl;
- //cout << um.find(200) << endl;
-
- }
-
- void test_unordered_map1()
- {
- unordered_map<int, int> um;
- um.insert({ 1,1 });
- um.insert({ 2,1 });
- um.insert({ 3,1 });
- um.insert({ 4,1 });
- um.insert({ 5,1 });
- um.insert({ 6,1 });
- um[7];
- um[8];
- um[9];
- um[10];
- um[11]++;
- unordered_map<int, int>::iterator it = um.begin();
- while (it != um.end())
- {
- cout << it->first << " ";
- ++it;
- }
- cout << endl;
- }
-
- void test_unordered_map2()
- {
- string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
- "苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };
- unordered_map<string, int> countMap;
- for (auto& e : arr)
- {
- countMap[e]++;
- }
-
- for (auto e : countMap)
- {
- cout << e.first << ":" << e.second << endl;
- }
- cout << endl;
- }
-
-
- };
- #pragma once
- #include"HashTable.h"
-
- namespace my_unorded_set {
-
- template<class K, class Hash = hashFunc<K> >
- class unorded_set
- {
- public:
-
- struct SetKeyOfT
- {
- const K& operator()(const K& key)
- {
- return key;
- }
- };
-
-
-
-
- typedef typename hash_bucket::HashTable< K,const K, Hash, SetKeyOfT>::iterator iterator;
- iterator beigin()
- {
- return _ht.begin();
- }
-
- iterator end()
- {
- 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:
- hash_bucket::HashTable<K, const K, Hash, SetKeyOfT> _ht;
- };
-
- void test_unordered_set()
- {
- unorded_set<int> us;
- us.insert(1);
- us.insert(2);
- us.insert(3);
- us.insert(4);
- us.insert(6);
- us.insert(7);
- //cout << us.find(1) << endl;
- //cout << us.find(100) << endl;
- cout << "//" << endl;
- cout << us.erase(100) << endl;
- cout << us.erase(1) << endl;
-
- }
-
- void test_unordered_set1()
- {
- unorded_set<int> us;
- us.insert(99);
- us.insert(4);
- us.insert(6);
- us.insert(7);
- us.insert(9);
- us.insert(2);
- us.insert(3);
- us.insert(11);
- us.insert(1);
- us.insert(96);
- us.insert(16);
- us.insert(56);
- us.insert(50);
- us.insert(57);
- us.insert(58);
- us.erase(11);
-
- unorded_set<int>::iterator it = us.beigin();
- while (it != us.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
-
- }
-
-
-
- };
- #include"unordered_set.h"
- #include"unordered_map.h"
- int main()
- {
- //hash_bucket::test_hash_bucket1();
- cout << "unordered_set" << ":";
- my_unorded_set::test_unordered_set1();
- cout << "unordered_map" << ":";
- my_unordered_map::test_unordered_map1();
- cout << "unordered_map方括号重载测试" << ":" << endl;;
- my_unordered_map::test_unordered_map2();
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。