目录
一、改造哈希表
1.1 节点定义
1.2 哈希表迭代器相关
1.3 哈希表接口相关
二、unordered_set模拟实现代码
三、unordered_map模拟实现代码
使用的代码是之前篇章哈希表的代码,改造后哈希表代码如下:
#pragma once
#include
#include //开散列(哈希桶)template
struct HashNode
{T _data;HashNode* _next;HashNode(const T& data):_data(data), _next(nullptr){}
};//仿函数
template
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};//仿函数特化
template<>
struct HashFunc
{size_t operator()(const string& key){size_t hash = 0;for (auto ch : key){hash *= 131;//BKDRHash算法hash += ch;}return hash;}
};//声明HashTable,不声明__HTIerator的_ht变量找不到标识符
template
class HashTable;template
struct __HTIerator
{typedef HashNode Node;typedef __HTIerator Self;typedef HashTable HT;//成员变量Node* _node;HT* _ht;//构造函数__HTIerator(Node* node, HT* ht):_node(node),_ht(ht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& s)const{return _node != s._node;}Self& operator++(){if (_node->_next)//当前桶没有走完,迭代遍历当前桶{_node = _node->_next;}else//当前桶走完了,要找下一个桶的第一个{KeyOfT kot;Hash 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;}else//桶为空,继续找下一个桶{++hashi;}//后面没有桶了,哈希表已经遍历完if (hashi == _ht->_tables.size()){_node = nullptr;}}}return *this;}
};template
class HashTable
{typedef HashNode Node;//要给__HTIerator类设置成友元,否则__HTIerator类无法访问HashTable的私有成员,ps;_ht->_tables.size()template friend struct __HTIerator;
public:typedef __HTIerator iterator;//构造HashTable():_n(0){_tables.resize(10);//默认开10个空间}//析构~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;}}iterator begin(){for (size_t i = 0; i < _tables.size(); ++i){if (_tables[i]){return iterator(_tables[i], this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}//插入pair Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if (it != end())//查询插入的值是否已经存在{return make_pair(it, false);//存在插入失败}//大于标定负载因子,就需要扩容//这里负载因子标定为 1if (_tables.size() == _n){//创建一个新的哈希表,新哈希表的大小设置为原哈希表的2倍vector newTables;newTables.resize(_tables.size() * 2);//将原哈希表当中的结点插入到新哈希表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;//有效数据+1++_n;return make_pair(iterator(newNode, this), true);}//查找iterator Find(const K& key){size_t hashi = Hash()(key) % _tables.size();//计算key的哈希地址Node* cur = _tables[hashi];//遍历这个桶进行查找while (cur){if (KeyOfT()(cur->_data) == key)//查找成功{return iterator(cur, this);}cur = cur->_next;}//该桶遍历完,查找失败return end();}//删除bool Erase(const K& key){size_t hashi = Hash()(key) % _tables.size();//计算key的哈希地址Node* prev = nullptr;//用于记录 cur的前一个节点Node* cur = _tables[hashi];while (cur){if (KeyOfT()(cur->_data) == key)//找到需要删除的节点{if (cur == _tables[hashi])//头删{_tables[hashi] = cur->_next;}else//中间删除{prev->_next = cur->_next;}delete cur;--_n;return true;}else//迭代遍历{prev = cur;cur = cur->_next;}}//删除失败return false;}
private:vector _tables;//指针数组, 哈希表size_t _n;//用于记录表中有效数据的个数
};
哈希表模板参数的控制:unordered_set是K模型的容器,而unordered_map是KV模型的容器
HashTable类的模板参数介绍:
template
模板参数K(key)用于查找和删除数据,模板参数T用于存储数据,如果上层是unordered_set,T则是key,如果上层是unordered_map,T则是键值对pair
模板参数Hash是一个哈希函数,在哈希表已经有过详细解释
模板参数KeyOfT 是一个仿函数,用于获取数据,由上层的 unordered_set 和 unordered_map 独立实现
数据类型是泛型,由上层传入才确定是Key 或键值对pair
template
struct HashNode
{T _data;HashNode* _next;HashNode(const T& data):_data(data), _next(nullptr){}
};
迭代器模板参数如下
template
哈希表的迭代器与其他容器的迭代器有所不同,哈希表的迭代器增加了一个 HashTable 的指针,不增加这个变量是无法完成哈希表的迭代器的,这也意味着 const 版本的迭代器需要重新写在另一个类,不能复用普通迭代器的代码完成const 迭代器
构造迭代器时,不仅需要对应哈希结点的指针,还需要该哈希结点所在哈希表的地址
//构造函数
__HTIerator(Node* node, HT* ht):_node(node),_ht(ht)
{}
前置++
Self& operator++()
{if (_node->_next)//当前桶没有走完,迭代遍历当前桶{_node = _node->_next;}else//当前桶走完了,要找下一个桶的第一个{KeyOfT kot;Hash 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;}else//桶为空,继续找下一个桶{++hashi;}//后面没有桶了,哈希表已经遍历完if (hashi == _ht->_tables.size()){_node = nullptr;}}}return *this;
}
};
其他都与之前一致,就不解释了
详细介绍在哈希表篇章,这里不解释了,这里只是对它的进口进行了封装
都是调用哈希表接口,不解释了
#include "HashTable.h"namespace fy
{template>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public://没有实例化,没办法到HashTable里面找iterator,所以typename就是告诉编译器这里是一个类型,实例化以后再去取typedef typename HashTable::iterator iterator;//typedef typename HashTable::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}/*const_iterator begin()const{return }*/bool erase(const K& key){return _ht.Erase(key);}pair insert(const K& key){return _ht.Insert(key);}private:HashTable _ht;};void Test_unorderSet(){unordered_set us;us.insert(4);us.insert(14);us.insert(8);us.insert(2);us.insert(18);us.insert(2);us.insert(234);us.insert(24);us.insert(11);us.insert(666);unordered_set::iterator it = us.begin();while (it != us.end()){cout << *it << " ";++it;}cout << endl;us.insert(5);us.insert(16);us.erase(4);us.erase(4);us.erase(2);us.erase(11);us.erase(666);us.erase(234);}
}
都是调用哈希表接口,不解释了
#include "HashTable.h"namespace fy
{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& data){return _ht.Insert(data);}iterator find(const K& key){return _ht.Find(key);}bool erase(const K& key){return _ht.Erase(key);}V& operator[](const K& key){pair ret = _ht.Insert(make_pair(key, V()));//无法直接返回,要使用变量进行接收再返回return ret.first->second;}private:HashTable, Hash, MapKeyOfT> _ht;};void Test_unorderedMap(){string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };unordered_map countMap;for (auto& e : arr){countMap[e]++;}for (const auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
}
文章内容都是炒旧饭,没啥介绍的,文章就到这
----------------我是分割线---------------
文章到这里就结束了,下一篇即将更新
下一篇:多表关联练习