目录
一.unordered_map和unordered_set
1.时间复杂度:它们查找的时间复杂度平均都是O(1)
2.它们的底层结构相同,都使用哈希桶
简单的使用代码:
二.哈希
1. 直接定址法--(数分布集中 常用)
2. 除留余数法--(数分布不集中,均匀 常用)
3.哈希冲突
(2)闭散列
线性探测:
添加状态
二次探测
4.负载因子
5.哈希冲突的处理方法和哈希函数 区分
阶段一:只有插入的哈希
阶段二:完善哈希表,雏形哈希桶
1.当key是string或其他类型,如何映射?
2.仿函数中的特化
3.开散列(哈希桶)
阶段三:完善哈希桶
析构函数
优化insert
总代码:
阶段四:模拟实现unorderedmap/set
(1)template 第二个模板T
(2)class KeyOfT模板参数:
(3)HashFunc模板 要放到 UnorderedSet.h / UnorderedMap.h 中
UnorderedSet.h
UnorderedMap.h
HashTable.h
Test.cpp
unordered_map和unordered_set和map/set用法一样,不同就是unordered_map和unordered_set不排序,map/set自动排序
哈希是通过哈希函数来计算元素的存储位置的,找的时候同样通过哈希函数找元素位 置,不需要循环遍历因此时间复杂度为O(1)
3.它们在进行元素插入时,不需要比较key找待插入元素的位置,只需要通过哈希函数,就可以确认元素需要存储的位置。
#include
#include
#include
哈希(散列)-映射:存储关键字跟存储位置建立关联关系
值和存储位置——> 映射关系 ——> 查找按映射关系去找
哈希是一种用来进行高效查找的数据结构,查找的时间复杂度平均为O(1)
采用哈希方式解决问题时,必须使用哈希函数
哈希查找的时间复杂度不一定是O(1)(因为存在哈希冲突,一般基本都是O(1))
哈希是以牺牲空间为代价,提高查询的效率(采用哈希处理时,一般所需空间都会比元素个数多,否则产生冲突的概率就比较大,影 响哈希的性能)
直接建立映射关系问题:
1、数据范围分布很广、不集中(除留余数法解决)
2、key的数据不是整数,是字符串怎么办?是自定义类型对象怎么办?
除留余数法会有个问题,就是哈希冲突
例如:除留余数法中,20%表大小10=0,把20放在下标0处,如果有30,50呢,30,50%10也得放在下标0处,所以要解决哈希冲突,解决哈希冲突两种常见的方法是:闭散列和开散列。
从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
hash(key)%N + i(i= 0,1,2,3...):比如10%N(N是表大小10)=0,因为下标0已经放了20,则再加i=1,10%10+1=1,就把10放到了下标是1的地方。30:放30就是30%10+2=2,把30放到了下标是2的地方
缺点:我占你的,你占他的,拥堵
enum State
{
EMPTY,
EXITS,
DELETE
};
hash(key)%N + i² (i= 0,1,2,3...)
比如10%N(N是表大小10)=0,因为下标0已经放了20,则再加 i² =1,10%10+1=1,就把10放到了下标是1的地方。30:放30就是30%10+2²=4,把30放到了下标是4的地方
对上面方法的优化:不那么拥堵
starti %= _tables.size();还是.capacity() ?
_tables.size()是能存数据的位置个数,_tables.capacity()是总容量大小(包括没有初始化的空间)类似resize和reserve的区别,starti %= _tables.size(); 插入数据只能插在能插入的位置,因为_tables[hashi] 的operator[] 会自动检查这个位置是否有值还是没有值的随机数,有值就可以插入。
负载因子(散列表的载荷因子α) = 填入表中的元素个数 / 哈希表的长度
由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,表明填入表中的元素越多,产生冲突的可能性就越大:反之,α越小,标明填入表中的元素越少,产生冲突的可能性就越小。
哈希函数作用是:建立元素与其存储位置之前的对应关系的,在存储元素时,先通过哈希函数计算元素在哈希表格中的存储位置,然后存储元素。好的哈希函数可以减少冲突的概率,但是不能绝对避免哈希函数,万一发生哈希冲突,得需要借助哈希冲突处理方法来 解决。
常见的哈希函数有:直接定址法、除留余数法、平方取中法、随机数法、数字分析法、叠加法等
常见哈希冲突处理:闭散列(线性探测、二次探测)、开散列(链地址法)、多次散列
#pragma once
#include
enum State
{
EMPTY,
EXITS,
DELETE
};
// 休息21:16继续
template
struct HashData
{
pair _kv;
State _state;
};
template
class HashTable
{
public:
bool Insert(const pair& kv)
{
// 负载因子到0.7及以上,就扩容。这里算 负载因子>0.7,因为是小数,前面可能需要类型转换,否则除出来是0,我们不如把 负载因子*10>7
if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
{
size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// 扩容以后,需要重新映射
HashTable newHT;
newHT._tables.resize(newSize);
// 遍历旧表,插入newHT
for (auto& e : _tables)
{
if (e._state == EXITS)
{
newHT.Insert(e._kv);
}
}
newHT._tables.swap(_tables);
}
size_t starti = kv.first;
starti %= _tables.size();
size_t hashi = starti;
size_t i = 1;
// 线性探测/二次探测
while (_tables[hashi]._state == EXITS)
{
hashi = starti + i;
++i;
hashi %= _tables.size();
}
_tables[hashi]._kv = kv;
_tables[hashi]._state = EXITS;
_n++;
}
HashData* Find(const K& key);
bool Erase(const K& key);
private:
vector _tables;
size_t _n = 0; // 存储关键字个数
};
key是string或者自定义类型时,需要转成整数再映射进哈希表,这个转成整数的方法有多样,比如:string类型转成整数方法是BKDR法,hash乘一个值131再加字符串的ASCII值,这样能减少冲突。如果仅仅是通过整个字符串的ASCII值来来映射,比如"abc"和"bac"就一样,一样就是哈希冲突,哈希冲突也没关系,我们有处理哈希冲突的机制:把映射到同一位置的值像下一个位置放(线性探测)。类似于先放20后再放10,映射在大小是10的哈希表中,都放在0处,哈希冲突了,就可以把10放在20后面的空位置上。
template<>
struct DefaultHash
{
size_t operator()(const string& key)
{ //key是string类型用仿函数DefaultHash转成整数
//转换方法:BKDR ,数学研究出来的
size_t hash = 0;
for (auto ch : key)
{
hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
}
return hash;
}
};
这样模拟实现unorodered_map时,直接定义 unorodered_map
template
struct DefaultHash
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct DefaultHash
{
size_t operator()(const string& key)
{ //key是string类型用仿函数DefaultHash转成整数
//转换方法:BKDR ,数学研究出来的
size_t hash = 0;
for (auto ch : key)
{
hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
}
return hash;
}
};
template>
class HashTable
{
typedef HashData Data;
public:
……
#pragma once
#include
namespace CloseHash
{
enum State
{
EMPTY,
EXITS,
DELETE
};
template
struct HashData
{
pair _kv;
State _state = EMPTY;
};
template
struct DefaultHash
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct DefaultHash
{
size_t operator()(const string& key)
{ //key是string类型用仿函数DefaultHash转成整数
//转换方法:BKDR ,数学研究出来的
size_t hash = 0;
for (auto ch : key)
{
hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
}
return hash;
}
};
//struct StringHash
//{
// // "abcd"
// // "aa"
// size_t operator()(const string& key)
// {
// //return key[0]; 可以
// //return (size_t)&key; 不行
//
// /*size_t hash = 0;
// for (auto ch : key)
// {
// hash += ch;
// }
//
// return hash;*/
//
// // BKDR
// size_t hash = 0;
// for (auto ch : key)
// {
// hash = hash* 131 + ch;
// }
//
// return hash;
// }
//};
template>
class HashTable
{
typedef HashData Data;
public:
bool Insert(const pair& kv)
{
if (Find(kv.first)) //去冗余 易错点1,容易忘写
{
return false;
}
// 负载因子到0.7及以上,就扩容
if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
{
size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// 扩容以后,需要重新映射
HashTable newHT;
newHT._tables.resize(newSize);
// 遍历旧表,插入newHT
for (auto& e : _tables)
{
if (e._state == EXITS) //易错点2,容易忘写
{
newHT.Insert(e._kv);
}
}
newHT._tables.swap(_tables);
}
HashFunc hf;
size_t starti = hf(kv.first);
starti %= _tables.size();
size_t hashi = starti;
size_t i = 1;
// 线性探测/二次探测
while (_tables[hashi]._state == EXITS)
{
hashi = starti + i;
++i;
hashi %= _tables.size();
}
_tables[hashi]._kv = kv;
_tables[hashi]._state = EXITS;
_n++;
return true;
}
Data* Find(const K& key)
{
if (_tables.size() == 0) //哈希表还没数据时不能查找
{
return nullptr;
}
HashFunc hf;
size_t starti = hf(key); //不同类型的key通过对应的仿函数转成整数-
starti %= _tables.size();//-比如key是string类型,就-
size_t hashi = starti; //-用仿函数DefaultHash转成整形
size_t i = 1;
while (_tables[hashi]._state != EMPTY)
{ //下面如果状态是DELETE就无法被查找到
if (_tables[hashi]._state != DELETE && _tables[hashi]._kv.first == key)
{
return &_tables[hashi];
}
hashi = starti + i;
++i;
hashi %= _tables.size();
}
return nullptr;
}
bool Erase(const K& key)
{
Data* ret = Find(key);
if (ret)
{
ret->_state = DELETE;
--_n;
return true;
}
else
{
return false;
}
}
private:
vector _tables;
size_t _n = 0; // 存储关键字个数
};
void TestHT1()
{
int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
//HashTable> ht;
HashTable ht;
if (ht.Find(10))
{
cout << "找到了10" << endl;
}
for (auto e : a)
{
ht.Insert(make_pair(e, e));
}
// 测试扩容
ht.Insert(make_pair(15, 15));
ht.Insert(make_pair(5, 5));
ht.Insert(make_pair(15, 15));
if (ht.Find(50))
{
cout << "找到了50" << endl;
}
if (ht.Find(10))
{
cout << "找到了10" << endl;
}
ht.Erase(10);
ht.Erase(10);
if (ht.Find(50))
{
cout << "找到了50" << endl;
}
if (ht.Find(10))
{
cout << "找到了10" << endl;
}
}
void TestHT2()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
/*string s1("苹果");
string s2("果苹");
string s3("果果");
string s4("萍果");
string s5("abcd");
string s6("bcad");
string s7("aadd");
StringHash hf;
cout << hf(s1) << endl;
cout << hf(s2) << endl;
cout << hf(s3) << endl;
cout << hf(s4) << endl << endl;
cout << hf(s5) << endl;
cout << hf(s6) << endl;
cout << hf(s7) << endl;*/
//HashTable countHT;
HashTable countHT;
for (auto& str : arr)
{
auto ret = countHT.Find(str);
if (ret)
{
ret->_kv.second++;
}
else
{
countHT.Insert(make_pair(str, 1));
}
}
// 对应类型配一个仿函数,仿函数对象实现把key对象转换成映射的整数
//HashTable countHT;
//HashTable countHT;
HashTable copy(countHT);
}
}
namespace Bucket
{
template
struct HashNode
{
pair _kv;
HashNode* _next;
HashNode(const pair& kv)
:_kv(kv)
, _next(nullptr)
{}
};
template
class HashTable
{
typedef HashNode Node;
public:
bool Insert(const pair& kv)
{
if (Find(kv.first))
{
return false;
}
// 负载因子 == 1 扩容
if (_tables.size() == _n)
{
// 扩容,有缺陷,可以再优化,大家下去可以思考一下
size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
HashTable newHT;
newHT._tables.resize(newSize, nullptr);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur)
{
newHT.Insert(cur->_kv);
cur = cur->_next;
}
}
newHT._tables.swap(_tables);
}
size_t hashi = kv.first;
hashi %= _tables.size();
// 头插到对应的桶即可
Node* newnode = new Node(kv);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return true;
}
Node* Find(const K& key)
{
if (_tables.size() == 0)
{
return nullptr;
}
size_t hashi = key;
hashi %= _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
return cur;
}
cur = cur->_next;
}
return nullptr;
}
private:
// 指针数组
vector _tables;
size_t _n = 0;
};
void TestHT1()
{
int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
//HashTable> ht;
HashTable ht;
if (ht.Find(10))
{
cout << "找到了10" << endl;
}
for (auto e : a)
{
ht.Insert(make_pair(e, e));
}
// 测试扩容
ht.Insert(make_pair(15, 15));
ht.Insert(make_pair(5, 5));
ht.Insert(make_pair(15, 15));
ht.Insert(make_pair(25, 15));
ht.Insert(make_pair(35, 15));
ht.Insert(make_pair(45, 15));
}
}
新增析构函数,优化版insert,删除Erase,拷贝构造,赋值(自己写),Hashfunc(string也可以%)
vector是自定义类型会去调用自己的析构函数析构数组,但不会释放每个位置上的链表的节点,我们要一个一个释放每个链表的每个节点
~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;
}
}
开一个新的扩容后大小的数组newTable,把原数组_table的节点一个一个头插到新数组中,这样就避免原方法中的双重消耗:新开节点(插入数组),释放旧数组节点
bool Insert(const T& data)
{
HashFunc hf;
KeyOfT kot;
if (Find(kot(data)))
{
return false;
}
// 负载因子 == 1 扩容
if (_tables.size() == _n)
{
size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
vector newTable; //开一个新的扩容后大小的数组
newTable.resize(newSize, nullptr); //开一个新的扩容后大小的数组
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hf(kot(cur->_data)) % newSize;
cur->_next = newTable[hashi];
newTable[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
newTable.swap(_tables);
}
size_t hashi = hf(kot(data));
hashi %= _tables.size();
// 头插到对应的桶即可
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return true;
}
#pragma once
#include
template
struct DefaultHash
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct DefaultHash
{
size_t operator()(const string& key)
{
// BKDR
size_t hash = 0;
for (auto ch : key)
{
hash = hash * 131 + ch;
}
return hash;
}
};
namespace Bucket
{
template
struct HashNode
{
pair _kv;
HashNode* _next;
HashNode(const pair& kv)
:_kv(kv)
, _next(nullptr)
{}
};
template>
class HashTable
{
typedef HashNode Node;
public:
~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;
}
}
bool Insert(const pair& kv)
{
if (Find(kv.first))
{
return false;
}
HashFunc hf;
// 负载因子 == 1 扩容
if (_tables.size() == _n)
{
// 扩容,有缺陷,可以再优化,大家下去可以思考一下
/*size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
HashTable newHT;
newHT._tables.resize(newSize, nullptr);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur)
{
newHT.Insert(cur->_kv);
cur = cur->_next;
}
}
newHT._tables.swap(_tables);*/
size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
vector newTable;
newTable.resize(newSize, nullptr);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hf(cur->_kv.first) % newSize;
cur->_next = newTable[hashi];
newTable[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
newTable.swap(_tables);
}
size_t hashi = hf(kv.first);
hashi %= _tables.size();
// 头插到对应的桶即可
Node* newnode = new Node(kv);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return true;
}
Node* Find(const K& key)
{
if (_tables.size() == 0)
{
return nullptr;
}
HashFunc hf;
size_t hashi = hf(key);
//size_t hashi = HashFunc()(key);
hashi %= _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
return cur;
}
cur = cur->_next;
}
return nullptr;
}
bool Erase(const K& key)
{
if (_tables.size() == 0)
{
return false;
}
HashFunc hf;
size_t hashi = hf(key);
hashi %= _tables.size();
Node* prev = nullptr;
Node* cur = _tables[hashi];
while (cur)
{
if (cur->_kv.first == key)
{
if (prev == nullptr)
{
_tables[hashi] = cur->_next;
}
else
{
prev->_next = cur->_next;
}
delete cur;
return true;
}
prev = cur;
cur = cur->_next;
}
return false;
//size_t hashi = key;
//hashi %= _tables.size();
//Node* cur = _tables[hashi];
//while (cur)
//{
// if (cur->_kv.first == key)
// {
// if (cur->_next == nullptr)
// {
// cur->_kv = _tables[hashi]->_kv;
// Node* first = _tables[hashi];
// _tables[hashi] = first->_next;
// delete first;
// }
// else
// {
// //....
// }
// return true;
// }
// prev = cur;
// cur = cur->_next;
//}
//return false;
}
private:
// 指针数组
vector _tables;
size_t _n = 0;
};
void TestHT1()
{
int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
//HashTable> ht;
HashTable ht;
if (ht.Find(10))
{
cout << "找到了10" << endl;
}
for (auto e : a)
{
ht.Insert(make_pair(e, e));
}
ht.Erase(20);
ht.Erase(10);
ht.Erase(30);
ht.Erase(50);
// 测试扩容
ht.Insert(make_pair(15, 15));
ht.Insert(make_pair(5, 5));
ht.Insert(make_pair(15, 15));
ht.Insert(make_pair(25, 15));
ht.Insert(make_pair(35, 15));
ht.Insert(make_pair(45, 15));
}
void TestHT2()
{
int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
HashTable ht;
for (auto e : a)
{
ht.Insert(make_pair(e, e));
}
// 需要自己实现拷贝构造,完成链表桶深拷贝
//HashTable copy(ht);
}
void TestHT3()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
//HashTable countHT;
HashTable countHT;
for (auto& str : arr)
{
auto ret = countHT.Find(str);
if (ret)
{
ret->_kv.second++;
}
else
{
countHT.Insert(make_pair(str, 1));
}
}
}
}
作用是让同一份哈希桶的代码能区分unordered_set和unordered_map,如果是unordered_set模板T就传入K,unordered_map模板T就传入pair
用于取出map中的pair中的K,取set中的key
KeyOfT kot;
if (Find(kot(data)))
{
return false;
}
因为K可能是 Date 这些自定义类型,无法直接比较,要写个仿函数DateHash,test_set() 中定义对象时就传这个仿函数unordered_set
pragma once
#include "HashTable.h"
namespace bit
{
// 21:06
template>
class unordered_set
{
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
public:
typedef typename Bucket::HashTable::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair 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:
Bucket::HashTable _ht;
};
struct Date
{
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
bool operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
int _year;
int _month;
int _day;
};
struct DateHash
{
size_t operator()(const Date& d)
{
//return d._year + d._month + d._day;
size_t hash = 0;
hash += d._year;
hash *= 131;
hash += d._month;
hash *= 1313;
hash += d._day;
//cout << hash << endl;
return hash;
}
};
void test_set()
{
unordered_set s;
//set s;
s.insert(2);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(12);
//unordered_set::iterator it = s.begin();
unordered_set::iterator it;
it = s.begin();
//auto it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
unordered_set sd;
sd.insert(Date(2022, 3, 4));
sd.insert(Date(2022, 4, 3));
}
}
#pragma once
#include "HashTable.h"
namespace bit
{
template>
class unordered_map
{
struct MapKeyOfT
{
const K& operator()(const pair& kv)
{
return kv.first;
}
};
public:
typedef typename Bucket::HashTable, MapKeyOfT, HashFunc>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
pair insert(const pair& 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 ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
Bucket::HashTable, MapKeyOfT, HashFunc> _ht;
};
void test_map()
{
unordered_map dict;
dict.insert(make_pair("sort", ""));
dict.insert(make_pair("left", ""));
dict.insert(make_pair("left", "ʣ"));
dict["string"];
dict["left"] = "ʣ";
dict["string"] = "ַ";
unordered_map::iterator it = dict.begin();
while (it != dict.end())
{
cout << it->first << " " << it->second << endl;
++it;
}
cout << endl;
for (auto& kv : dict)
{
cout << kv.first << " " << kv.second << endl;
}
}
}
#pragma once
#include
template
struct DefaultHash
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct DefaultHash
{
size_t operator()(const string& key)
{
// BKDR
size_t hash = 0;
for (auto ch : key)
{
hash = hash * 131 + ch;
}
return hash;
}
};
namespace Bucket
{
template
struct HashNode
{
T _data;
HashNode* _next;
HashNode(const T& data)
:_data(data)
, _next(nullptr)
{}
};
template
class HashTable;
template
class __HTIterator
{
typedef HashNode Node;
typedef __HTIterator Self;
public:
Node* _node;
HashTable* _pht;
__HTIterator() {} //默认构造函数和非默认构造函数重载
__HTIterator(Node* node, HashTable* pht)
:_node(node)
, _pht(pht)
{}
Self& operator++()
{
if (_node->_next)
{
_node = _node->_next;
}
else
{
KeyOfT kot;
HashFunc hf;
size_t hashi = hf(kot(_node->_data)) % _pht->_tables.size();
++hashi;
//找下一个不为空的桶
for (; hashi < _pht->_tables.size(); ++hashi)
{
if (_pht->_tables[hashi])
{
_node = _pht->_tables[hashi];
break;
}
}
// 没有找到不为空的桶,用nullptr去做end标识
if (hashi == _pht->_tables.size())
{
_node = nullptr;
}
}
return *this;
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s) const
{
return _node != s._node;
}
bool operator==(const Self& s) const
{
return _node == s._node;
}
};
// unordered_map ->HashTable, MapKeyOfT> _ht;
// unordered_set ->HashTable _ht;
template
class HashTable
{
template
friend class __HTIterator;
typedef HashNode Node;
public:
typedef __HTIterator iterator;
iterator begin()
{
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
if (cur)
{
return iterator(cur, this);
}
}
return end();
}
iterator end()
{
return iterator(nullptr, this);
}
~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;
}
}
size_t GetNextPrime(size_t prime)
{
const int PRIMECOUNT = 28;
static 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
};
// 获取比prime大那一个素数
size_t i = 0;
for (; i < PRIMECOUNT; ++i)
{
if (primeList[i] > prime)
return primeList[i];
}
return primeList[i];
}
pair Insert(const T& data)
{
HashFunc hf;
KeyOfT kot;
iterator pos = Find(kot(data));
if (pos != end())
{
return make_pair(pos, false);
}
// 负载因子 == 1 扩容
if (_tables.size() == _n)
{
//size_t newSize = _tables.size() == 0 ? 11 : _tables.size() * 2;
size_t newSize = GetNextPrime(_tables.size());
if (newSize != _tables.size())
{
vector newTable;
newTable.resize(newSize, nullptr);
for (size_t i = 0; i < _tables.size(); ++i)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->_next;
size_t hashi = hf(kot(cur->_data)) % newSize;
cur->_next = newTable[hashi];
newTable[hashi] = cur;
cur = next;
}
_tables[i] = nullptr;
}
newTable.swap(_tables);
}
}
size_t hashi = hf(kot(data));
hashi %= _tables.size();
// 头插到对应的桶即可
Node* newnode = new Node(data);
newnode->_next = _tables[hashi];
_tables[hashi] = newnode;
++_n;
return make_pair(iterator(newnode, this), false);;
}
iterator Find(const K& key)
{
if (_tables.size() == 0)
{
return iterator(nullptr, this);
}
KeyOfT kot;
HashFunc hf;
size_t hashi = hf(key);
//size_t hashi = HashFunc()(key);
hashi %= _tables.size();
Node* cur = _tables[hashi];
while (cur)
{
if (kot(cur->_data) == key)
{
return iterator(cur, this);
}
cur = cur->_next;
}
return iterator(nullptr, this);
}
bool Erase(const K& key)
{
if (_tables.size() == 0)
{
return false;
}
HashFunc hf;
KeyOfT kot;
size_t hashi = hf(key);
hashi %= _tables.size();
Node* prev = nullptr;
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;
}
prev = cur;
cur = cur->_next;
}
return false;
}
private:
// 指针数组
vector _tables;
size_t _n = 0;
};
}
#include
#include
#include