编程语言
199
一、哈希的基本介绍
性能上
查找数据,哈希时间复杂度为O(1) 大量随机数据插入,map和set性能快 大量随机数据删除,map和set性能快
哈希映射方法
1.直接定址法:每个值都有自己唯一的位置 优点:无哈希冲突,简单且均匀 缺点:如果空间分散会有很大的空间浪费 适应于范围小且连续的情况
2,除留余数法 优点:空间合适且不浪费过多的空间 缺点:可能存在有相同的映射地址(哈希冲突)
哈希大部分都围绕着解决哈希冲突
1.闭散列:冲突后占用下一个空位置 缺点:查找效率变低,互相冲突导致插入逻辑复杂 线性探测:线性找下一个空位置,++ 二次探测:平方步调找下一个位置,^2
2.开散列(哈希桶)(拉链法):冲突的位置下加上链表(哈希桶),由此STL中并没有双向迭代器 缺点:链表太长有效率问题 优点:冲突不会相互影响
二、闭散列的哈希表实现
实际中开散列应用情况优于闭散列,点一下
#include<iostream> #include<vector> using namespace std; template<class K> struct KeyToCmp //得到可以进行比较的数据 { size_t operator()(const K& k) { return (size_t)k; } }; template<> struct KeyToCmp<string> //特化一个string的仿函数,使得字符也能有其映射的地址 { size_t operator()(const string& k) { size_t hash = 0; for (auto& e : k) { hash += e; } return hash; } }; enum State { EMPTY, EXIST, DELETE }; template<class K,class V> struct HashData { pair<K,V> _kv; State _state = EMPTY; //通过加入状态成员来方便进行增删查改 }; template<class K,class V,class Hash = KeyToCmp<int>> class HashTable { public: typedef HashData<K,V> Data; HashTable() //构造 { _tables.resize(10); } //负载因子 = 表中数据/表的大小 衡量哈希表满的程度 //负载因子越大,插入数据越容易发生哈希冲突,冲突越多,效率越低 //所以哈希表并不是满了才增容,一般负载因子在0.7左右就开始增容 //控制的太小,会使得空间浪费 bool Insert(const pair<K,V>& kv) { if (Find(kv.first) != nullptr) //已经有了 return false; if (_num * 10 / _tables.size() >= 7) //负载因子大了,开始增容 { HashTable<K, V, Hash> tmp; tmp._tables.resize(2 * _tables.size()); //新创建一个哈希表,将其数组空间扩大至旧哈希表的两倍 for (auto& e : _tables) { if (e._state == EXIST) //如果有数据 tmp.Insert(e._kv); //插入,扩容已经在上面完成了,递归时判断增容条件进不去,不会造成死循环 } _tables.swap(tmp._tables); } Hash get; size_t index = get(kv.first) % _tables.size(); while (_tables[index]._state == EXIST) { index++; index %= _tables.size(); } _tables[index]._kv = kv; _tables[index]._state = EXIST; _num++; return true; } Data* Find(const K& k) { Hash get; size_t index = get(k) % _tables.size(); while (_tables[index]._state != EMPTY) { if (_tables[index]._state == EXIST && _tables[index]._kv.first == k) return &_tables[index]; index++; index %= _tables.size(); } return nullptr; //哈希冲突是连续的,如果第一个后面为空,说明后面也没有冲突的情况,直接返回 } bool Erase(const pair<K, V>& kv) { Data* ret = Find(kv.first); if (ret) { ret->_state = DELETE; --_num; return true; } else { return false; } } private: vector<Data> _tables; size_t _num = 0; //存储当前存储有效数据的个数 };
三、开散列模拟实现unordered_xxx
unordered_xxx底层都是由开散列实现的
#pragma once #include<iostream> #include<vector> using namespace std; namespace BsyOpen { template<class K> struct GetWay { const K& operator()(const K& k) { return k; } }; template<> //特化处理字符串,使得其返回值能够被模 struct GetWay<string> { size_t operator()(const string& k) { size_t way = 0; for (size_t i = 0; i < k.size(); ++i) { way *= 131; //BKDR Hash,使得哈希冲突降低 way += k[i]; } return way; } }; template<class T> //一个哈希表节点 struct HashNode { T _data; HashNode<T>* _next; //我们自己实现的哈希输出,并非输入的顺序,如果要实现按输入顺序输出就要多指针 //HashNode<T>* _linknext; //以按顺序输出 //HashNode<T>* _linkprev; //以删除 HashNode(const T& data) :_data(data) ,_next(nullptr) {} }; // 前置声明一下, 否则在迭代器类中, 无法识别出HashTable是什么 template<class K, class T, class TheKey, class GetWayy> //哈希表 class HashTable; template<class K,class T,class TheKey,class GetWayy> //哈希表迭代器 struct __HashIterator { typedef __HashIterator< K, T, TheKey, GetWayy> Self; typedef HashTable<K, T, TheKey, GetWayy> HT; typedef HashNode<T> Node; Node* _node; HT* _pht; //以便在后续operator++中起到作用 __HashIterator(Node* node,HT* pht) :_node(node) ,_pht(pht) {} T& operator*() { return _node->_data; } T* operator->() { return &_node->_data; } Self operator++() { if (_node->_next) { _node = _node->_next; //如果桶没走完接着往下走 } else { TheKey thekey; GetWayy getway; //计算数值应该位于哪个头节点 size_t i = getway(thekey(_node->_data)) % _pht->_tables.size(); //size_t i = GetWayy()(TheKey()(_node->_data)) % _pht->_tables.size(); i++; //现在i就是下一个需要验证是否为空的数组下标 for (; i < _pht->_tables.size(); ++i) { Node* cur = _pht->_tables[i]; if (cur) { _node = cur; return *this; } } //此时说明没有下一个节点了 _node = nullptr; } return *this; } bool operator!=(const Self& s) { return _node != s._node; } }; template<class K,class T,class TheKey,class GetWayy> //哈希表 class HashTable { public: friend struct __HashIterator< K, T, TheKey, GetWayy>; typedef HashNode<T> Node; typedef __HashIterator< K, T, TheKey,GetWayy> 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() //销毁完桶之后,,析构自动销毁this,即vector会随着析构函数销毁 { Clear(); } void Clear() { 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; //使得无法访问每个头节点 } } size_t GetNextPrime(size_t num) //这是一种算法,使得可以减小哈希冲突 { const int primecount = 28; const size_t primelist[primecount] = //二倍关系质数表 { 53,97,193,389,769, 1543,3079,6151,12289,24593, 49157,98317,1996613,393241,786433, 1572869,3145739,6291469,12582917,25165843, 50331653,100663319,201326611,402653189,805306457, 1610612741,3221225473,4294967291 }; for (size_t i = 0; i < primecount; ++i) { if (primelist[i] > num) { return primelist[i]; } } return primelist[primecount - 1]; //如果还大,就一直返回最后一个 } pair<iterator,bool> Insert(const T& data) { TheKey thekey; GetWayy getway; if (_num == _tables.size()) //此时负载因子为1,避免以后造成大量冲突,增容 { vector<Node*> newtable; size_t newsize = GetNextPrime(_num); newtable.resize(newsize); for (size_t i = 0; i < _tables.size(); ++i) { Node* cur = _tables[i]; //取每个头 while (cur) { //旧表取下重新计算新表中的位置 Node* next = cur->_next; size_t index = getway(thekey(cur->_data)) % newtable.size(); //新表中的位置 cur->_next = newtable[index]; //头插进去 newtable[index] = cur; cur = next; } _tables[i] = nullptr; } _tables.swap(newtable); } size_t index = getway(thekey(data)) % _tables.size(); Node* cur = _tables[index]; while (cur) { if (thekey(cur->_data) == thekey(data)) //重复了,不插入,结束 return make_pair(iterator(cur,this),false); else cur = cur->_next; } //检验完后走到这里说明可以插入了 //选择头插,因为有头节点的位置,不用找尾 Node* newnode = new Node(data); newnode->_next = _tables[index]; _tables[index] = newnode; ++_num; return make_pair(iterator(newnode, this), true); } Node* Find(const K& k) { TheKey thekey; GetWayy getway; size_t index = getway(k) % _tables.size(); Node* cur = _tables[index]; while (cur) { if (thekey(cur->_data) == k) return cur; else cur = cur->_next; } return nullptr; } bool Erase(const K& k) { TheKey thekey; GetWayy getway; size_t index = getway(k) % _tables.size(); Node* prev = nullptr; Node* cur = _tables[index]; //找到需要找到数据的头 while (cur) { if (thekey(cur->_data) == k)//找到了 { if (prev == nullptr) //说明要删除的对象就是第一个节点 _tables[index] = 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 _num = 0; }; }
1.unordered_set
#pragma once #include"Open_Hash.hpp" namespace Bsy { template<class K, class GetWayy = BsyOpen::GetWay<K>> class unordered_set { struct TheKeyOfSet { const K& operator()(const K& k) { return k; } }; public: typedef typename BsyOpen::HashTable<K, K, TheKeyOfSet, GetWayy>::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } pair<iterator,bool> insert(const K& k) { return _ht.Insert(k); } private: BsyOpen::HashTable<K, K, TheKeyOfSet,GetWayy> _ht; }; void test_unordered_set1() { unordered_set<int> s; s.insert(1); s.insert(5); s.insert(6); s.insert(2); unordered_set<int>::iterator it = s.begin(); while (it != s.end()) { cout << *it << " "; ++it; } cout << endl; } }
2.unordered_map
#pragma once #include"Open_Hash.hpp" namespace Bsy { template<class K,class V, class GetWayy = BsyOpen::GetWay<K>> class unordered_map { struct TheKeyOfMap { const K& operator()(const pair<K, V>& kv) { return kv.first; } }; public: typedef typename BsyOpen::HashTable<K, pair<K,V>, TheKeyOfMap, GetWayy>::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); } V& operator[](const K& k) { //pair<iterator, bool> ret = _ht.Insert(make_pair(k, V())); //return ret.first->second; return _ht.Insert(make_pair(k, V())).first->second; } private: BsyOpen::HashTable<K, pair<K, V>, TheKeyOfMap,GetWayy> _ht; }; void test_unordered_map1() { unordered_map<string, string> dict; dict.insert(make_pair("sort", "paixu")); dict.insert(make_pair("left", "zuo")); dict.insert(make_pair("long", "chang")); dict["left"] = "you"; dict["hash"] = "haxi"; unordered_map<string, string>::iterator it = dict.begin(); while (it != dict.end()) { cout << it->first <<":"<<it->second<< endl; ++it; } cout << endl; } }