哈希
哈希也可以叫散列
画一个哈希表
哈希冲突越多,哈希表效率越低。
闭散列开放定址法:
1.线性探测,依次往后去找下一个空位置。
2.二次探测,按2次方往后找空位置。
#pragma once
#include<vector>
#include<iostream>
#include<string>
#include<unordered_map>
using namespace std;
template<class K>
struct SetKeyOfT
{const K& operator()(const K& key){return key;}
};
//unordered_set<K> ->HashTable<K,K>
//unordered_map<K,V> ->HashTable<K,pair<K,V>>
enum State
{EMPTY,EXITS,DELETE,
};
template<class T>
struct HashData//解决:该位置原来就是空还是删除后变为了空
{T _data;State _state;
};
template<class K,class T,class KeyOfT>
class HashTable//冲突了一定是连续的,但是删除会影响查找,所以应该设置状态标志。将T替换为HashData
{typedef HashData<T> HashData;
public:bool Insert(const T& d){KeyOfT koft;//负载因子=表中数据/表的大小 衡量哈希表满的程度//表越接近满,插入数据越容易冲突,冲突越多,效率越低。//哈希表并不是满了才增容,开放定址法中,一般负载因子到了0.7左右就开始增容//负载因子越小,冲突概率越低,整体效率越高,但是负载因子越小,浪费的空间越大。//所以负载因子一般去折中值if (_tables.size()==0||_num*10/_tables.size()>=7){//增容//1.开二倍大小的新表//2.遍历旧表中的数据,确定数据在新表中的映射位置//3.释放旧表vector<HashData> newtables;size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;newtables.resize(_tables.size() * 2);for (size_t i = 0; i < _tables.size(); ++i){if (_tables[i]._state == EXITS){size_t index = koft(_tables[i]._data % newtables.size());while (newtables[index]._state == EXITS){++index;if (index == _tables.size()){index = 0;}}newtables[index] = _tables[i];}}_tables.swap(newtables);}//KeyOfT koft;//计算d中的key在表中映射的位置size_t index = koft(d) % _tables.size();//EXITS就进行探测while (_tables[index]._state==EXITS){if (koft(_tables[index]._data) == koft(d))return false;++index;if (index == _tables.size()){index = 0;}}_tables[index]._data = d;_tables[index]._state = EXITS;_num++;return true;}HashData* Find(const K& key){KeyOfT koft;//计算d在表中映射的位置size_t index = key % _tables.size();while (_tables[index]._state != EMPTY){if (koft(_tables[index]._data) == key){if (_tables[index]._state == EXITS)return &_tables[index];else if (_tables[index]._state == DELETE)return nullptr;}++index;if (index == _tables.size()){index = 0;}}return nullptr;}bool Erase(const K& key){HashData* ret = Find(key);if (ret){ret->_state = DELETE;--_num;return true;}else{return false;}}
private:vector<HashData> _tables;size_t _num=0;//存了几个有效数据
};void TestHashTable()
{HashTable<int, int, SetKeyOfT<int>> ht;ht.Insert(4);ht.Insert(14);ht.Insert(24);ht.Insert(5);ht.Insert(15);ht.Insert(25);ht.Insert(6);ht.Insert(16);}
更优的实现方法
开散列哈希桶:
namespace OPEN_HASH
{template<class T>struct HashNode{T _data;HashNode<T>* _next;};template<class K,class T,class KeyOfT>class HashTable{public:typedef HashNode<T>* Node;bool Insert(const T& data){//1.先查找这个值在不在表中KeyOfT koft;//如果负载因子等于1,则增容,避免大量的哈希冲突if (_tables.size == _num){//增容//1.开二倍大小的新表//2.遍历旧表中的数据,确定数据在新表中的映射位置//3.释放旧表vector<Node> newtables;size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;newtables.resize(newsize);for (size_t i = 0; i < _tables.size(); ++i){//将旧表中的结点取下来重新计算在新表中的位置,并插入进去Node cur = _tables[i];while (cur){Node next = cur->_next;size_t index = koft(cur->_data) % newtables.size();cur->_next = newtables[index];newtables[index] = cur;cur = next;}_tables[i] = nullptr;}_tables[i].swap(newtables);}//计算数据在表中映射的位置size_t index = koft(data) % _tables.size();Node cur = _tables[index];while (cur){if (koft(cur->_data) == koft(data))return false;elsecur = cur->next;}//2.头插到挂的链表中Node newnode = new Node(data);newnode->next = _tables[index];_tables[index] = newnode;++_num;return true;}Node* Find(const K& key){KeyOfT koft;size_t index = key % _tables.size();Node cur = _tables[index];while (cur){if (koft(cur->_data) == key){return cur;}else return cur->_next;}return nullptr;}bool Erase(const K& key){KeyOfT koft;size_t index = key % _tables.size();Node prev = nullptr;Node cur = _tables[index];while (cur){if (koft(cur->_data) == key){if (prev == nullptr)//表示要删的值在一个结点_tables[index] = cur->_next;elseprev->_next = cur->_next;delete cur;return true;}else{prev = cur;cur = cur->_next;}}return false;}private:vector<Node> _tables;size_t _num=0;//记录表中存储的数据个数};
}