This commit is contained in:
mmlhbjk 2024-09-28 02:21:54 +08:00
commit 77b0d6f6de
46 changed files with 2774 additions and 0 deletions

685
Linux/HashTable.hpp Normal file
View File

@ -0,0 +1,685 @@
#pragma once
#include <iostream>
#include <vector>
#include <string>
// unordered_set<K> -> HashTable<K, K>
// unordered_map<K, V> -> HashTable<K, pair<K, V>>
namespace Lenyiin
{
template <class K>
struct SetKeyOfT
{
const K &operator()(const K &key)
{
return key;
}
};
enum State
{
EMPTY, // 槽位为空
EXIST, // 槽位已经存在一个元素
DELETE // 槽位中元素被删除
};
template <class T>
struct HashData
{
T _data;
State _state;
HashData()
: _data(T()), _state(EMPTY)
{
}
};
template <class K, class T, class KeyOfT>
class Close_HashTable
{
private:
typedef struct HashData<T> HashData;
size_t SecondHash(const K &key, size_t table_size)
{
return 1 + (key % (table_size - 1));
}
public:
// 负载因子 = 表中数据/表的大小 衡量哈希表满的程度
// 表越接近满, 插入数据越容易冲突, 冲突越多, 效率越低
// 哈希表并不是满了才增容, 开放定制法中, 一般负载因子到 0.7 左右就开始增容
// 负载因子越小, 冲突概率越低, 整体效率越高, 但是负载因子越小, 浪费的空间越大, 所以负载因子一般取一个折中的值
void CheckCapacity()
{
KeyOfT koft;
// // version 1
// if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
// {
// // 增容
// // 1. 开 2倍大小的新表
// // 2. 遍历旧表的数据,重新计算在新表中位置
// // 3. 释放旧表
// std::vector<HashData> newtables;
// size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// newtables.resize(newsize);
// for (size_t i = 0; i < _tables.size(); i++)
// {
// if (_tables[i]._state == EXIST)
// {
// // 计算在新表中的位置, 并处理冲突
// size_t index = koft(_tables[i]._data) % newtables.size();
// while (newtables[index]._state == EXIST)
// {
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// newtables[index] = _tables[i];
// }
// }
// _tables.swap(newtables);
// }
// // version 2
// if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
// {
// // 增容
// // 1. 开 2倍大小的新表
// // 2. 遍历旧表的数据,重新计算在新表中位置
// // 3. 释放旧表
// std::vector<HashData> newtables;
// size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// newtables.resize(newsize);
// for (size_t i = 0; i < _tables.size(); i++)
// {
// if (_tables[i]._state == EXIST)
// {
// // 重新计算新表中的位置
// size_t index = koft(_tables[i]._data) % newtables.size();
// size_t step = SecondHash(koft(_tables[i]._data), newtables.size());
// // 处理冲突:双重哈希探测
// while (newtables[index]._state == EXIST)
// {
// index = (index + step) % newtables.size();
// }
// // 插入元素到新表
// newtables[index] = _tables[i];
// }
// }
// _tables.swap(newtables);
// }
// version 3
// 另一种增容思路
if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
{
Close_HashTable<K, T, KeyOfT> newht;
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
newht._tables.resize(newsize);
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i]._state == EXIST)
{
newht.Insert(_tables[i]._data);
}
}
_tables.swap(newht._tables);
}
}
bool Insert(const T &data)
{
KeyOfT koft;
CheckCapacity();
// 闭散列中线性探测有什么问题?
// 线性探测思路就是我的位置被占了, 我就挨着往后去占别人的位置, 可能会导致一片一片的冲突, 洪水效应
// version 1
// 线性探测
// 计算 data 中的 key 在表中映射的位置
// size_t index = koft(data) % _tables.size();
// while (_tables[index]._state == EXIST)
// {
// if (koft(_tables[index]._data) == koft(data))
// {
// return false; // 已经存在
// }
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// version 2
// 二次探测
// 计算 data 中的 key 在表中映射的位置
// size_t start = koft(data) % _tables.size();
// size_t index = start;
// int i = 0;
// while (_tables[index]._state == EXIST)
// {
// if (koft(_tables[index]._data) == koft(data))
// {
// return false; // 已经存在
// }
// index = start + i * i;
// i++;
// index %= _tables.size();
// }
// version 3
// 双重哈希
size_t index = koft(data) % _tables.size();
size_t step = SecondHash(koft(data), _tables.size());
while (_tables[index]._state == EXIST)
{
if (koft(_tables[index]._data) == koft(data))
{
return false; // 如果找到相同的 key插入失败
}
index = (index + step) % _tables.size(); // 使用双重哈希计算下一个位置
}
_tables[index]._data = data;
_tables[index]._state = EXIST;
++_num;
// 我么可以看到闭散列-开放定制法不是一种好的解决方式, 因为它是一种我的位置被占了, 我就去抢占别人的位置的思路
// 也就是说他的哈希冲突会相互影响, 我冲突占你的, 你冲突占他的, 他冲突了... , 没完没了, 整体的效率都变低了
// 开散列的哈希桶可以解决上面的问题
return true;
}
// 线性探测
// HashData *Find(const K &key)
// {
// KeyOfT koft;
// // 计算 data 中的 key 在表中映射的位置
// size_t index = key % _tables.size();
// while (_tables[index]._state != EMPTY)
// {
// if (koft(_tables[index]._data) == key)
// {
// if (_tables[index]._state == EXIST)
// {
// return &_tables[index];
// }
// else if (_tables[index]._state == DELETE)
// {
// return nullptr;
// }
// }
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// return nullptr;
// }
// 双重哈希
HashData *Find(const K &key)
{
KeyOfT koft;
size_t index = key % _tables.size();
size_t step = SecondHash(key, _tables.size()); // 计算步长
while (_tables[index]._state != EMPTY)
{
if (koft(_tables[index]._data) == key)
{
if (_tables[index]._state == EXIST)
{
return &_tables[index];
}
else if (_tables[index]._state == DELETE)
{
return nullptr;
}
}
index = (index + step) % _tables.size(); // 使用双重哈希探测下一个位置
}
return nullptr;
}
bool Erase(const K &key)
{
HashData *ret = Find(key);
if (ret)
{
ret->_state = DELETE;
--_num;
return true;
}
else
{
return false;
}
}
HashData &getHashData(int pos)
{
return _tables[pos];
}
void Print()
{
int size = _tables.size();
for (int i = 0; i < size; i++)
{
std::cout << i << "\t";
}
std::cout << std::endl;
for (int i = 0; i < size; i++)
{
auto cur = _tables[i];
if (cur._state == EXIST)
{
std::cout << cur._data << "\t";
}
else
{
std::cout << "*\t";
}
}
std::cout << "\n\n";
}
private:
std::vector<HashData> _tables;
size_t _num = 0; // 存储了几个有效数据
};
template <class T>
struct HashNode
{
T _data; // 存储数据
HashNode<T> *_next; // 存储下一个节点
// 如果想要实现迭代顺序为插入顺序, 可以加两个指针组成一个链表
// HashNode<T>* _linknext;
// HashNode<T>* _linkprev;
HashNode(const T &data)
: _data(data), _next(nullptr)
{
}
};
// 前置声明
template <class K, class T, class KeyOfT, class Hash>
class Open_HashTable;
// 哈希表只有单向迭代器, 只有 ++, 没有--
template <class K, class T, class KeyOfT, class Hash>
struct __HashTableIterator
{
typedef __HashTableIterator<K, T, KeyOfT, Hash> Self;
typedef Open_HashTable<K, T, KeyOfT, Hash> HT;
typedef HashNode<T> Node;
Node *_node;
HT *_pht;
__HashTableIterator(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
{
// 如果一个桶走完了, 找到下一个桶继续便利
KeyOfT koft;
size_t index = _pht->HashFunc(koft(_node->_data)) % _pht->_tables.size();
++index;
while (index < _pht->_tables.size())
{
Node *cur = _pht->_tables[index];
if (cur)
{
_node = cur;
return *this;
}
++index;
}
_node = nullptr;
}
return *this;
}
Self operator++(int)
{
Self tmp(*this);
++*this;
return tmp;
}
bool operator!=(const Self &s)
{
return _node != s._node;
}
bool operator==(const Self &s)
{
return _node == s._node;
}
};
template <class K>
struct _Hash
{
const K &operator()(const K &key)
{
return key;
}
};
// 特化
template <>
struct _Hash<std::string>
{
size_t operator()(const std::string &key)
{
// BKDR Hash
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++)
{
hash *= 131;
hash += key[i];
}
return hash;
}
};
struct _HashString
{
size_t operator()(const std::string &key)
{
// BKDR Hash
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++)
{
hash *= 131;
hash += key[i];
}
return hash;
}
};
template <class K, class T, class KeyOfT, class Hash>
// template <class K, class T, class KeyOfT, class Hash = _Hash<K>>
class Open_HashTable
{
private:
typedef HashNode<T> Node;
public:
friend struct __HashTableIterator<K, T, KeyOfT, Hash>;
typedef __HashTableIterator<K, T, KeyOfT, Hash> 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);
}
Open_HashTable()
{
}
Open_HashTable(size_t bucket_count)
: _tables(bucket_count), _num(0)
{
}
~Open_HashTable()
{
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 HashFunc(const K &key)
{
Hash hash;
return hash(key);
}
size_t GetNextPrime(size_t num)
{
const int PrimeSize = 28;
static const unsigned long PrimeList[PrimeSize] =
{
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};
for (size_t i = 0; i < PrimeSize; i++)
{
if (PrimeList[i] > num)
{
return PrimeList[i];
}
}
return PrimeList[PrimeSize - 1]; // 如果已经是最后一个数的, 则不增容
}
// 重新哈希
void Rehash(size_t newsize)
{
KeyOfT koft;
std::vector<Node *> newtables;
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 = HashFunc(koft(cur->_data)) % newtables.size();
cur->_next = newtables[index];
newtables[index] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newtables);
}
// 插入操作
// 当大量的数据冲突, 这些哈希冲突的数据就会挂在同一个链式桶中, 查找时效率就会降低, 所以开散列-哈希桶也是要控制哈希冲突的。
// 如何控制呢? 通过控制负载因子, 不过这里就把空间利用率提高一些, 负载因子也可以高一些, 一般开散列把负载因子控制到1, 会比较好一点
std::pair<iterator, bool> Insert(const T &data)
{
KeyOfT koft;
// 1. 检查负载因子
// 如果负载因子等于 1 , 则增容, 避免大量的哈希冲突
if (_tables.size() == _num)
{
// 1. 开两倍大小的新表(不一定是两倍)
// 2. 遍历旧表的数据, 重新计算在新表中的位置
// 3. 释放旧表
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// size_t newsize = GetNextPrime(_tables.size());
Rehash(newsize);
}
// 2. 计算数据在表中映射的位置
size_t index = HashFunc(koft(data)) % _tables.size();
// 3. 先查找这个值在不在表中, 是否有冲突
Node *cur = _tables[index];
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(koft(data)))
{
// 如果已经存在该键,返回失败
return std::make_pair(iterator(cur, this), false);
}
else
{
// 查找下一个节点
cur = cur->_next;
}
}
// 4. 头插挂到链表中(尾插也是可以的)
Node *newnode = new Node(data);
newnode->_next = _tables[index];
_tables[index] = newnode;
++_num; // 更新已存储元素数量
return std::make_pair(iterator(newnode, this), true);
}
// 查找操作
Node *Find(const K &key)
{
KeyOfT koft;
// 1. 计算键在表中映射的位置
size_t index = HashFunc(key) % _tables.size();
Node *cur = _tables[index];
// 2. 遍历链表查找匹配的键
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(key))
{
// 如果找到匹配的元素,返回其指针
return cur;
}
// 继续查找下一个节点
cur = cur->_next;
}
// 如果未找到,返回空指针
return nullptr;
}
bool Erase(const K &key)
{
KeyOfT koft;
// 1. 计算要删除元素的哈希值
size_t index = HashFunc(key) % _tables.size();
Node *prev = nullptr;
Node *cur = _tables[index];
// 2. 遍历链表, 查找匹配的元素
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(key))
{
// 3. 找到元素后, 调整链表结构
if (prev == nullptr)
{
// 如果要删除的元素是链表的第一个节点, 直接让桶指向下一个节点
_tables[index] = cur->_next;
}
else
{
// 否则,将前一个节点的 next 指向当前节点的下一个节点
prev->_next = cur->_next;
}
// 4. 释放节点内存
delete cur;
--_num; // 元素数量减少
return true;
}
else
{
// 继续遍历链表
prev = cur;
cur = cur->_next;
}
}
// 如果未找到该元素,返回 false
return false;
}
void Print() const
{
KeyOfT koft;
int size = _tables.size();
for (int i = 0; i < size; i++)
{
std::cout << i << "\t";
Node *cur = _tables[i];
while (cur)
{
std::cout << koft(cur->_data) << "\t";
cur = cur->_next;
}
std::cout << std::endl;
}
std::cout << std::endl;
}
private:
std::vector<Node *> _tables; // 哈希表存储桶
size_t _num; // 记录着存储的数据个数
};
}

324
Linux/Main.cc Normal file
View File

@ -0,0 +1,324 @@
#include "Unordered_Set.hpp"
#include "Unordered_Map.hpp"
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <time.h>
void Test_unordered_set()
{
std::unordered_set<int> us; // Java里面取名叫HashSet
us.insert(4);
us.insert(2);
us.insert(1);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
us.insert(5);
us.insert(6);
us.insert(3);
us.insert(5);
us.insert(6);
us.insert(3);
us.insert(15);
us.insert(16);
us.insert(13);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
us.insert(15);
us.insert(16);
us.insert(13);
us.insert(9);
us.insert(8);
us.insert(10);
us.insert(7);
us.insert(12);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
// 会去重,但是不会自动排序
std::unordered_set<int>::iterator it = us.begin();
while (it != us.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
std::set<int> s; // Java里面取名叫TreeSet
s.insert(4);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(6);
s.insert(3);
s.insert(5);
s.insert(6);
s.insert(3);
s.insert(15);
s.insert(16);
s.insert(13);
s.insert(15);
s.insert(16);
s.insert(13);
s.insert(9);
s.insert(8);
s.insert(10);
s.insert(7);
s.insert(12);
// set会去重会自动排序
std::set<int>::iterator its = s.begin();
while (its != s.end())
{
std::cout << *its << " ";
++its;
}
std::cout << std::endl;
}
void Test_unordered_map()
{
std::unordered_map<std::string, std::string> dict;
dict.insert(std::make_pair("hello", "你好"));
dict.insert(std::make_pair("world", "世界"));
dict.insert(std::make_pair("apple", "苹果"));
dict.insert(std::make_pair("orange", "橘子"));
dict.insert(std::make_pair("banana", "香蕉"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("sort", "排序"));
dict["string"] = "字符串";
std::unordered_map<std::string, std::string>::iterator it = dict.begin();
while (it != dict.end())
{
std::cout << it->first << " " << it->second << std::endl;
++it;
}
std::cout << std::endl;
std::map<std::string, std::string> mdict;
mdict.insert(std::make_pair("hello", "你好"));
mdict.insert(std::make_pair("world", "世界"));
mdict.insert(std::make_pair("apple", "苹果"));
mdict.insert(std::make_pair("orange", "橘子"));
mdict.insert(std::make_pair("banana", "香蕉"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("sort", "排序"));
mdict["string"] = "字符串";
std::map<std::string, std::string>::iterator mit = mdict.begin();
while (mit != mdict.end())
{
std::cout << mit->first << " " << mit->second << std::endl;
++mit;
}
}
void Test_OP()
{
std::unordered_set<int> us;
std::set<int> s;
const size_t n = 100000;
std::vector<int> v;
v.reserve(n); // reserve()函数是vector预留空间的但是并不真正创建元素对象。
// resize()函数是开空间+初始化
srand(time(0));
for (size_t i = 0; i < n; ++i)
{
v.push_back(rand());
}
// 插入
clock_t begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.insert(v[i]);
}
clock_t end = clock();
std::cout << "unordered_set insert time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.insert(v[i]);
}
end = clock();
std::cout << "set insert time:\t\t" << end - begin << std::endl;
// 查找
begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.find(v[i]);
}
end = clock();
std::cout << "unordered_set find time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.find(v[i]);
}
end = clock();
std::cout << "set find time:\t\t\t" << end - begin << std::endl;
// 删除
begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.erase(v[i]);
}
end = clock();
std::cout << "unordered_set erase time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.erase(v[i]);
}
end = clock();
std::cout << "set erase time:\t\t\t" << end - begin << std::endl;
}
void Test_Close_HashTable()
{
Lenyiin::Close_HashTable<int, int, Lenyiin::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);
ht.Print();
ht.Erase(14);
ht.Erase(24);
ht.Print();
std::cout << "是否存在 5 ? " << (ht.Find(5) ? "true" : "false") << std::endl;
}
void Test_Open_HashTable_1()
{
Lenyiin::Open_HashTable<int, int, Lenyiin::SetKeyOfT<int>, Lenyiin::_Hash<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);
ht.Insert(26);
ht.Insert(36);
ht.Insert(33);
ht.Insert(37);
ht.Insert(32);
ht.Print();
ht.Erase(4);
ht.Erase(14);
ht.Print();
}
void Test_Open_HashTable_2()
{
Lenyiin::Open_HashTable<std::string, std::string, Lenyiin::SetKeyOfT<std::string>, Lenyiin::_Hash<std::string>> ht;
ht.Insert("sort");
ht.Insert("string");
ht.Insert("left");
ht.Insert("right");
std::cout << ht.HashFunc("abcd") << std::endl;
std::cout << ht.HashFunc("aadd") << std::endl;
}
void Test_Unordered_Set()
{
Lenyiin::Unordered_Set<int> s;
s.Insert(1);
s.Insert(5);
s.Insert(4);
s.Insert(2);
s.Insert(5);
s.Insert(5);
s.Insert(53);
s.Insert(54);
Lenyiin::Unordered_Set<int>::iterator it = s.begin();
while (it != s.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
s.Print();
std::cout << "查找 4: " << (s.Find(4) ? "true" : "false") << std::endl;
std::cout << "查找 6: " << (s.Find(6) ? "true" : "false") << std::endl;
std::cout << "删除 2" << std::endl;
s.Erase(2);
s.Print();
}
void Test_Unordered_Map()
{
Lenyiin::Unordered_Map<std::string, std::string> dict;
dict.Insert(std::make_pair("sort", "排序"));
dict.Insert(std::make_pair("left", "左边"));
dict.Insert(std::make_pair("string", "字符串"));
dict["left"] = "剩余"; // 修改
dict["end"] = "尾部"; // 插入 + 修改
// Unordered_Map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
// cout << it->first << ":" << it->second << endl;
std::cout << (*it).first << ":" << (*it).second << std::endl;
++it;
}
std::cout << std::endl;
dict.Print();
std::cout << "查找 left: " << (dict.Find("left") ? "true" : "false") << std::endl;
std::cout << "查找 up: " << (dict.Find("up") ? "true" : "false") << std::endl;
std::cout << "删除 left" << std::endl;
dict.Erase("left");
dict.Print();
}
int main()
{
// Test_unordered_set();
// Test_unordered_map();
// Test_OP();
// Test_Close_HashTable();
// Test_Open_HashTable_1();
// Test_Open_HashTable_2();
// Test_Unordered_Set();
Test_Unordered_Map();
return 0;
}

6
Linux/Makefile Normal file
View File

@ -0,0 +1,6 @@
main:Main.cc
g++ -o $@ $^
.PHONY:clean
clean:
rm -f main

74
Linux/Unordered_Map.hpp Normal file
View File

@ -0,0 +1,74 @@
#pragma once
#include "HashTable.hpp"
namespace Lenyiin
{
template <class K, class V, class Hash = _Hash<K>>
class Unordered_Map
{
public:
struct MapKOfT
{
const K &operator()(const std::pair<K, V> &kv)
{
return kv.first;
}
};
typedef typename Open_HashTable<K, std::pair<K, V>, MapKOfT, Hash>::iterator iterator;
iterator begin()
{
return _hashTable.begin();
}
iterator end()
{
return _hashTable.end();
}
// 构造函数: 指定哈希表的初始大小
Unordered_Map(size_t bucket_count = 10)
: _hashTable(bucket_count)
{
}
// 析构
~Unordered_Map()
{
}
std::pair<iterator, bool> Insert(const std::pair<K, V> &kv)
{
return _hashTable.Insert(kv);
}
V &operator[](const K &key)
{
std::pair<iterator, bool> ret = _hashTable.Insert(std::make_pair(key, V()));
return ret.first->second;
}
// 查找元素
bool Find(const K &key)
{
return _hashTable.Find(key);
}
// 删除元素
bool Erase(const K &key)
{
return _hashTable.Erase(key);
}
// 打印
void Print() const
{
_hashTable.Print();
}
private:
Open_HashTable<K, std::pair<K, V>, MapKOfT, Hash> _hashTable;
};
}

70
Linux/Unordered_Set.hpp Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include "HashTable.hpp"
namespace Lenyiin
{
template <class K, class Hash = _Hash<K>>
class Unordered_Set
{
public:
struct SetKOfT
{
const K &operator()(const K &key)
{
return key;
}
};
typedef typename Open_HashTable<K, K, SetKOfT, Hash>::iterator iterator;
iterator begin()
{
return _hashTable.begin();
}
iterator end()
{
return _hashTable.end();
}
// 构造函数: 指定哈希表的初始大小
Unordered_Set(size_t bucket_count = 10)
: _hashTable(bucket_count)
{
}
// 析构
~Unordered_Set()
{
}
// 插入元素
std::pair<iterator, bool> Insert(const K &key)
{
return _hashTable.Insert(key);
}
// 查找元素
bool Find(const K &key)
{
return _hashTable.Find(key);
}
// 删除元素
bool Erase(const K &key)
{
return _hashTable.Erase(key);
}
// 打印
void Print() const
{
_hashTable.Print();
}
private:
// 使用哈希表作为底层数据结构
Open_HashTable<K, K, SetKOfT, Hash> _hashTable;
};
}

BIN
Linux/main Normal file

Binary file not shown.

View File

@ -0,0 +1,100 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:HashTable.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Unordered_Set.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Unordered_Map.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 267,
"SelectedChildIndex": 3,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Main.cpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp",
"RelativeDocumentMoniker": "Main.cpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp*",
"RelativeToolTip": "Main.cpp*",
"ViewState": "AgIAACwBAAAAAAAAAAAowDcBAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2024-09-27T15:45:08.801Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Unordered_Map.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp",
"RelativeDocumentMoniker": "Unordered_Map.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp",
"RelativeToolTip": "Unordered_Map.hpp",
"ViewState": "AgIAACAAAAAAAAAAAAAowEkAAAABAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:59.73Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "Unordered_Set.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp",
"RelativeDocumentMoniker": "Unordered_Set.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp",
"RelativeToolTip": "Unordered_Set.hpp",
"ViewState": "AgIAABwAAAAAAAAAAAAowCAAAAAmAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:46.521Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "HashTable.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp",
"RelativeDocumentMoniker": "HashTable.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp",
"RelativeToolTip": "HashTable.hpp",
"ViewState": "AgIAAIACAAAAAAAAAAAowKwCAAABAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:25.257Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@ -0,0 +1,100 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Main.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:HashTable.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Unordered_Set.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}|Windows_Unordered_Set_Map.vcxproj|solutionrelative:Unordered_Map.hpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 267,
"SelectedChildIndex": 3,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Main.cpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp",
"RelativeDocumentMoniker": "Main.cpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Main.cpp",
"RelativeToolTip": "Main.cpp",
"ViewState": "AgIAAPAAAAAAAAAAAAAowP8AAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2024-09-27T15:45:08.801Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Unordered_Map.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp",
"RelativeDocumentMoniker": "Unordered_Map.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Map.hpp",
"RelativeToolTip": "Unordered_Map.hpp",
"ViewState": "AgIAACAAAAAAAAAAAAAowEkAAAABAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:59.73Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "Unordered_Set.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp",
"RelativeDocumentMoniker": "Unordered_Set.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\Unordered_Set.hpp",
"RelativeToolTip": "Unordered_Set.hpp",
"ViewState": "AgIAABwAAAAAAAAAAAAowCAAAAAmAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:46.521Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "HashTable.hpp",
"DocumentMoniker": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp",
"RelativeDocumentMoniker": "HashTable.hpp",
"ToolTip": "E:\\Git \u4ED3\u5E93\\\u516C\u5F00\u4ED3\u5E93\\10_unordered-set-map\\Windows_Unordered_Set_Map\\HashTable.hpp",
"RelativeToolTip": "HashTable.hpp",
"ViewState": "AgIAAIACAAAAAAAAAAAowKwCAAABAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2024-09-27T15:44:25.257Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@ -0,0 +1,685 @@
#pragma once
#include <iostream>
#include <vector>
#include <string>
// unordered_set<K> -> HashTable<K, K>
// unordered_map<K, V> -> HashTable<K, pair<K, V>>
namespace Lenyiin
{
template <class K>
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
enum State
{
EMPTY, // 槽位为空
EXIST, // 槽位已经存在一个元素
DELETE // 槽位中元素被删除
};
template <class T>
struct HashData
{
T _data;
State _state;
HashData()
: _data(T()), _state(EMPTY)
{
}
};
template <class K, class T, class KeyOfT>
class Close_HashTable
{
private:
typedef struct HashData<T> HashData;
size_t SecondHash(const K& key, size_t table_size)
{
return 1 + (key % (table_size - 1));
}
public:
// 负载因子 = 表中数据/表的大小 衡量哈希表满的程度
// 表越接近满, 插入数据越容易冲突, 冲突越多, 效率越低
// 哈希表并不是满了才增容, 开放定制法中, 一般负载因子到 0.7 左右就开始增容
// 负载因子越小, 冲突概率越低, 整体效率越高, 但是负载因子越小, 浪费的空间越大, 所以负载因子一般取一个折中的值
void CheckCapacity()
{
KeyOfT koft;
// // version 1
// if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
// {
// // 增容
// // 1. 开 2倍大小的新表
// // 2. 遍历旧表的数据,重新计算在新表中位置
// // 3. 释放旧表
// std::vector<HashData> newtables;
// size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// newtables.resize(newsize);
// for (size_t i = 0; i < _tables.size(); i++)
// {
// if (_tables[i]._state == EXIST)
// {
// // 计算在新表中的位置, 并处理冲突
// size_t index = koft(_tables[i]._data) % newtables.size();
// while (newtables[index]._state == EXIST)
// {
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// newtables[index] = _tables[i];
// }
// }
// _tables.swap(newtables);
// }
// // version 2
// if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
// {
// // 增容
// // 1. 开 2倍大小的新表
// // 2. 遍历旧表的数据,重新计算在新表中位置
// // 3. 释放旧表
// std::vector<HashData> newtables;
// size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// newtables.resize(newsize);
// for (size_t i = 0; i < _tables.size(); i++)
// {
// if (_tables[i]._state == EXIST)
// {
// // 重新计算新表中的位置
// size_t index = koft(_tables[i]._data) % newtables.size();
// size_t step = SecondHash(koft(_tables[i]._data), newtables.size());
// // 处理冲突:双重哈希探测
// while (newtables[index]._state == EXIST)
// {
// index = (index + step) % newtables.size();
// }
// // 插入元素到新表
// newtables[index] = _tables[i];
// }
// }
// _tables.swap(newtables);
// }
// version 3
// 另一种增容思路
if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
{
Close_HashTable<K, T, KeyOfT> newht;
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
newht._tables.resize(newsize);
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i]._state == EXIST)
{
newht.Insert(_tables[i]._data);
}
}
_tables.swap(newht._tables);
}
}
bool Insert(const T& data)
{
KeyOfT koft;
CheckCapacity();
// 闭散列中线性探测有什么问题?
// 线性探测思路就是我的位置被占了, 我就挨着往后去占别人的位置, 可能会导致一片一片的冲突, 洪水效应
// version 1
// 线性探测
// 计算 data 中的 key 在表中映射的位置
// size_t index = koft(data) % _tables.size();
// while (_tables[index]._state == EXIST)
// {
// if (koft(_tables[index]._data) == koft(data))
// {
// return false; // 已经存在
// }
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// version 2
// 二次探测
// 计算 data 中的 key 在表中映射的位置
// size_t start = koft(data) % _tables.size();
// size_t index = start;
// int i = 0;
// while (_tables[index]._state == EXIST)
// {
// if (koft(_tables[index]._data) == koft(data))
// {
// return false; // 已经存在
// }
// index = start + i * i;
// i++;
// index %= _tables.size();
// }
// version 3
// 双重哈希
size_t index = koft(data) % _tables.size();
size_t step = SecondHash(koft(data), _tables.size());
while (_tables[index]._state == EXIST)
{
if (koft(_tables[index]._data) == koft(data))
{
return false; // 如果找到相同的 key插入失败
}
index = (index + step) % _tables.size(); // 使用双重哈希计算下一个位置
}
_tables[index]._data = data;
_tables[index]._state = EXIST;
++_num;
// 我么可以看到闭散列-开放定制法不是一种好的解决方式, 因为它是一种我的位置被占了, 我就去抢占别人的位置的思路
// 也就是说他的哈希冲突会相互影响, 我冲突占你的, 你冲突占他的, 他冲突了... , 没完没了, 整体的效率都变低了
// 开散列的哈希桶可以解决上面的问题
return true;
}
// 线性探测
// HashData *Find(const K &key)
// {
// KeyOfT koft;
// // 计算 data 中的 key 在表中映射的位置
// size_t index = key % _tables.size();
// while (_tables[index]._state != EMPTY)
// {
// if (koft(_tables[index]._data) == key)
// {
// if (_tables[index]._state == EXIST)
// {
// return &_tables[index];
// }
// else if (_tables[index]._state == DELETE)
// {
// return nullptr;
// }
// }
// ++index;
// if (index == _tables.size())
// {
// index = 0;
// }
// }
// return nullptr;
// }
// 双重哈希
HashData* Find(const K& key)
{
KeyOfT koft;
size_t index = key % _tables.size();
size_t step = SecondHash(key, _tables.size()); // 计算步长
while (_tables[index]._state != EMPTY)
{
if (koft(_tables[index]._data) == key)
{
if (_tables[index]._state == EXIST)
{
return &_tables[index];
}
else if (_tables[index]._state == DELETE)
{
return nullptr;
}
}
index = (index + step) % _tables.size(); // 使用双重哈希探测下一个位置
}
return nullptr;
}
bool Erase(const K& key)
{
HashData* ret = Find(key);
if (ret)
{
ret->_state = DELETE;
--_num;
return true;
}
else
{
return false;
}
}
HashData& getHashData(int pos)
{
return _tables[pos];
}
void Print()
{
int size = _tables.size();
for (int i = 0; i < size; i++)
{
std::cout << i << "\t";
}
std::cout << std::endl;
for (int i = 0; i < size; i++)
{
auto cur = _tables[i];
if (cur._state == EXIST)
{
std::cout << cur._data << "\t";
}
else
{
std::cout << "*\t";
}
}
std::cout << "\n\n";
}
private:
std::vector<HashData> _tables;
size_t _num = 0; // 存储了几个有效数据
};
template <class T>
struct HashNode
{
T _data; // 存储数据
HashNode<T>* _next; // 存储下一个节点
// 如果想要实现迭代顺序为插入顺序, 可以加两个指针组成一个链表
// HashNode<T>* _linknext;
// HashNode<T>* _linkprev;
HashNode(const T& data)
: _data(data), _next(nullptr)
{
}
};
// 前置声明
template <class K, class T, class KeyOfT, class Hash>
class Open_HashTable;
// 哈希表只有单向迭代器, 只有 ++, 没有--
template <class K, class T, class KeyOfT, class Hash>
struct __HashTableIterator
{
typedef __HashTableIterator<K, T, KeyOfT, Hash> Self;
typedef Open_HashTable<K, T, KeyOfT, Hash> HT;
typedef HashNode<T> Node;
Node* _node;
HT* _pht;
__HashTableIterator(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
{
// 如果一个桶走完了, 找到下一个桶继续便利
KeyOfT koft;
size_t index = _pht->HashFunc(koft(_node->_data)) % _pht->_tables.size();
++index;
while (index < _pht->_tables.size())
{
Node* cur = _pht->_tables[index];
if (cur)
{
_node = cur;
return *this;
}
++index;
}
_node = nullptr;
}
return *this;
}
Self operator++(int)
{
Self tmp(*this);
++*this;
return tmp;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
};
template <class K>
struct _Hash
{
const K& operator()(const K& key)
{
return key;
}
};
// 特化
template <>
struct _Hash<std::string>
{
size_t operator()(const std::string& key)
{
// BKDR Hash
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++)
{
hash *= 131;
hash += key[i];
}
return hash;
}
};
struct _HashString
{
size_t operator()(const std::string& key)
{
// BKDR Hash
size_t hash = 0;
for (size_t i = 0; i < key.size(); i++)
{
hash *= 131;
hash += key[i];
}
return hash;
}
};
template <class K, class T, class KeyOfT, class Hash>
// template <class K, class T, class KeyOfT, class Hash = _Hash<K>>
class Open_HashTable
{
private:
typedef HashNode<T> Node;
public:
friend struct __HashTableIterator<K, T, KeyOfT, Hash>;
typedef __HashTableIterator<K, T, KeyOfT, Hash> 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);
}
Open_HashTable()
{
}
Open_HashTable(size_t bucket_count)
: _tables(bucket_count), _num(0)
{
}
~Open_HashTable()
{
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 HashFunc(const K& key)
{
Hash hash;
return hash(key);
}
size_t GetNextPrime(size_t num)
{
const int PrimeSize = 28;
static const unsigned long PrimeList[PrimeSize] =
{
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 };
for (size_t i = 0; i < PrimeSize; i++)
{
if (PrimeList[i] > num)
{
return PrimeList[i];
}
}
return PrimeList[PrimeSize - 1]; // 如果已经是最后一个数的, 则不增容
}
// 重新哈希
void Rehash(size_t newsize)
{
KeyOfT koft;
std::vector<Node*> newtables;
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 = HashFunc(koft(cur->_data)) % newtables.size();
cur->_next = newtables[index];
newtables[index] = cur;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newtables);
}
// 插入操作
// 当大量的数据冲突, 这些哈希冲突的数据就会挂在同一个链式桶中, 查找时效率就会降低, 所以开散列-哈希桶也是要控制哈希冲突的。
// 如何控制呢? 通过控制负载因子, 不过这里就把空间利用率提高一些, 负载因子也可以高一些, 一般开散列把负载因子控制到1, 会比较好一点
std::pair<iterator, bool> Insert(const T& data)
{
KeyOfT koft;
// 1. 检查负载因子
// 如果负载因子等于 1 , 则增容, 避免大量的哈希冲突
if (_tables.size() == _num)
{
// 1. 开两倍大小的新表(不一定是两倍)
// 2. 遍历旧表的数据, 重新计算在新表中的位置
// 3. 释放旧表
size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
// size_t newsize = GetNextPrime(_tables.size());
Rehash(newsize);
}
// 2. 计算数据在表中映射的位置
size_t index = HashFunc(koft(data)) % _tables.size();
// 3. 先查找这个值在不在表中, 是否有冲突
Node* cur = _tables[index];
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(koft(data)))
{
// 如果已经存在该键,返回失败
return std::make_pair(iterator(cur, this), false);
}
else
{
// 查找下一个节点
cur = cur->_next;
}
}
// 4. 头插挂到链表中(尾插也是可以的)
Node* newnode = new Node(data);
newnode->_next = _tables[index];
_tables[index] = newnode;
++_num; // 更新已存储元素数量
return std::make_pair(iterator(newnode, this), true);
}
// 查找操作
Node* Find(const K& key)
{
KeyOfT koft;
// 1. 计算键在表中映射的位置
size_t index = HashFunc(key) % _tables.size();
Node* cur = _tables[index];
// 2. 遍历链表查找匹配的键
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(key))
{
// 如果找到匹配的元素,返回其指针
return cur;
}
// 继续查找下一个节点
cur = cur->_next;
}
// 如果未找到,返回空指针
return nullptr;
}
bool Erase(const K& key)
{
KeyOfT koft;
// 1. 计算要删除元素的哈希值
size_t index = HashFunc(key) % _tables.size();
Node* prev = nullptr;
Node* cur = _tables[index];
// 2. 遍历链表, 查找匹配的元素
while (cur)
{
if (HashFunc(koft(cur->_data)) == HashFunc(key))
{
// 3. 找到元素后, 调整链表结构
if (prev == nullptr)
{
// 如果要删除的元素是链表的第一个节点, 直接让桶指向下一个节点
_tables[index] = cur->_next;
}
else
{
// 否则,将前一个节点的 next 指向当前节点的下一个节点
prev->_next = cur->_next;
}
// 4. 释放节点内存
delete cur;
--_num; // 元素数量减少
return true;
}
else
{
// 继续遍历链表
prev = cur;
cur = cur->_next;
}
}
// 如果未找到该元素,返回 false
return false;
}
void Print() const
{
KeyOfT koft;
int size = _tables.size();
for (int i = 0; i < size; i++)
{
std::cout << i << "\t";
Node* cur = _tables[i];
while (cur)
{
std::cout << koft(cur->_data) << "\t";
cur = cur->_next;
}
std::cout << std::endl;
}
std::cout << std::endl;
}
private:
std::vector<Node*> _tables; // 哈希表存储桶
size_t _num; // 记录着存储的数据个数
};
}

View File

@ -0,0 +1,324 @@
#include "Unordered_Set.hpp"
#include "Unordered_Map.hpp"
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <time.h>
void Test_unordered_set()
{
std::unordered_set<int> us; // Java里面取名叫HashSet
us.insert(4);
us.insert(2);
us.insert(1);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
us.insert(5);
us.insert(6);
us.insert(3);
us.insert(5);
us.insert(6);
us.insert(3);
us.insert(15);
us.insert(16);
us.insert(13);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
us.insert(15);
us.insert(16);
us.insert(13);
us.insert(9);
us.insert(8);
us.insert(10);
us.insert(7);
us.insert(12);
std::cout << "us 的 bucket 数量是: " << us.bucket_count()
<< "\tus 的 负载因子 是: " << us.load_factor() << std::endl;
// 会去重,但是不会自动排序
std::unordered_set<int>::iterator it = us.begin();
while (it != us.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
std::set<int> s; // Java里面取名叫TreeSet
s.insert(4);
s.insert(2);
s.insert(1);
s.insert(5);
s.insert(6);
s.insert(3);
s.insert(5);
s.insert(6);
s.insert(3);
s.insert(15);
s.insert(16);
s.insert(13);
s.insert(15);
s.insert(16);
s.insert(13);
s.insert(9);
s.insert(8);
s.insert(10);
s.insert(7);
s.insert(12);
// set会去重会自动排序
std::set<int>::iterator its = s.begin();
while (its != s.end())
{
std::cout << *its << " ";
++its;
}
std::cout << std::endl;
}
void Test_unordered_map()
{
std::unordered_map<std::string, std::string> dict;
dict.insert(std::make_pair("hello", "你好"));
dict.insert(std::make_pair("world", "世界"));
dict.insert(std::make_pair("apple", "苹果"));
dict.insert(std::make_pair("orange", "橘子"));
dict.insert(std::make_pair("banana", "香蕉"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("peach", "桃子"));
dict.insert(std::make_pair("sort", "排序"));
dict["string"] = "字符串";
std::unordered_map<std::string, std::string>::iterator it = dict.begin();
while (it != dict.end())
{
std::cout << it->first << " " << it->second << std::endl;
++it;
}
std::cout << std::endl;
std::map<std::string, std::string> mdict;
mdict.insert(std::make_pair("hello", "你好"));
mdict.insert(std::make_pair("world", "世界"));
mdict.insert(std::make_pair("apple", "苹果"));
mdict.insert(std::make_pair("orange", "橘子"));
mdict.insert(std::make_pair("banana", "香蕉"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("peach", "桃子"));
mdict.insert(std::make_pair("sort", "排序"));
mdict["string"] = "字符串";
std::map<std::string, std::string>::iterator mit = mdict.begin();
while (mit != mdict.end())
{
std::cout << mit->first << " " << mit->second << std::endl;
++mit;
}
}
void Test_OP()
{
std::unordered_set<int> us;
std::set<int> s;
const size_t n = 100000;
std::vector<int> v;
v.reserve(n); // reserve()函数是vector预留空间的但是并不真正创建元素对象。
// resize()函数是开空间+初始化
srand(time(0));
for (size_t i = 0; i < n; ++i)
{
v.push_back(rand());
}
// 插入
clock_t begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.insert(v[i]);
}
clock_t end = clock();
std::cout << "unordered_set insert time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.insert(v[i]);
}
end = clock();
std::cout << "set insert time:\t\t" << end - begin << std::endl;
// 查找
begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.find(v[i]);
}
end = clock();
std::cout << "unordered_set find time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.find(v[i]);
}
end = clock();
std::cout << "set find time:\t\t\t" << end - begin << std::endl;
// 删除
begin = clock();
for (size_t i = 0; i < n; ++i)
{
us.erase(v[i]);
}
end = clock();
std::cout << "unordered_set erase time:\t" << end - begin << std::endl;
begin = clock();
for (size_t i = 0; i < n; ++i)
{
s.erase(v[i]);
}
end = clock();
std::cout << "set erase time:\t\t\t" << end - begin << std::endl;
}
void Test_Close_HashTable()
{
Lenyiin::Close_HashTable<int, int, Lenyiin::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);
ht.Print();
ht.Erase(14);
ht.Erase(24);
ht.Print();
std::cout << "是否存在 5 ? " << (ht.Find(5) ? "true" : "false") << std::endl;
}
void Test_Open_HashTable_1()
{
Lenyiin::Open_HashTable<int, int, Lenyiin::SetKeyOfT<int>, Lenyiin::_Hash<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);
ht.Insert(26);
ht.Insert(36);
ht.Insert(33);
ht.Insert(37);
ht.Insert(32);
ht.Print();
ht.Erase(4);
ht.Erase(14);
ht.Print();
}
void Test_Open_HashTable_2()
{
Lenyiin::Open_HashTable<std::string, std::string, Lenyiin::SetKeyOfT<std::string>, Lenyiin::_Hash<std::string>> ht;
ht.Insert("sort");
ht.Insert("string");
ht.Insert("left");
ht.Insert("right");
std::cout << ht.HashFunc("abcd") << std::endl;
std::cout << ht.HashFunc("aadd") << std::endl;
}
void Test_Unordered_Set()
{
Lenyiin::Unordered_Set<int> s;
s.Insert(1);
s.Insert(5);
s.Insert(4);
s.Insert(2);
s.Insert(5);
s.Insert(5);
s.Insert(53);
s.Insert(54);
Lenyiin::Unordered_Set<int>::iterator it = s.begin();
while (it != s.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
s.Print();
std::cout << "查找 4: " << (s.Find(4) ? "true" : "false") << std::endl;
std::cout << "查找 6: " << (s.Find(6) ? "true" : "false") << std::endl;
std::cout << "删除 2" << std::endl;
s.Erase(2);
s.Print();
}
void Test_Unordered_Map()
{
Lenyiin::Unordered_Map<std::string, std::string> dict;
dict.Insert(std::make_pair("sort", "排序"));
dict.Insert(std::make_pair("left", "左边"));
dict.Insert(std::make_pair("string", "字符串"));
dict["left"] = "剩余"; // 修改
dict["end"] = "尾部"; // 插入 + 修改
// Unordered_Map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
// cout << it->first << ":" << it->second << endl;
std::cout << (*it).first << ":" << (*it).second << std::endl;
++it;
}
std::cout << std::endl;
dict.Print();
std::cout << "查找 left: " << (dict.Find("left") ? "true" : "false") << std::endl;
std::cout << "查找 up: " << (dict.Find("up") ? "true" : "false") << std::endl;
std::cout << "删除 left" << std::endl;
dict.Erase("left");
dict.Print();
}
int main()
{
Test_unordered_set();
Test_unordered_map();
Test_OP();
Test_Close_HashTable();
Test_Open_HashTable_1();
Test_Open_HashTable_2();
Test_Unordered_Set();
Test_Unordered_Map();
return 0;
}

View File

@ -0,0 +1,74 @@
#pragma once
#include "HashTable.hpp"
namespace Lenyiin
{
template <class K, class V, class Hash = _Hash<K>>
class Unordered_Map
{
public:
struct MapKOfT
{
const K& operator()(const std::pair<K, V>& kv)
{
return kv.first;
}
};
typedef typename Open_HashTable<K, std::pair<K, V>, MapKOfT, Hash>::iterator iterator;
iterator begin()
{
return _hashTable.begin();
}
iterator end()
{
return _hashTable.end();
}
// 构造函数: 指定哈希表的初始大小
Unordered_Map(size_t bucket_count = 10)
: _hashTable(bucket_count)
{
}
// 析构
~Unordered_Map()
{
}
std::pair<iterator, bool> Insert(const std::pair<K, V>& kv)
{
return _hashTable.Insert(kv);
}
V& operator[](const K& key)
{
std::pair<iterator, bool> ret = _hashTable.Insert(std::make_pair(key, V()));
return ret.first->second;
}
// 查找元素
bool Find(const K& key)
{
return _hashTable.Find(key);
}
// 删除元素
bool Erase(const K& key)
{
return _hashTable.Erase(key);
}
// 打印
void Print() const
{
_hashTable.Print();
}
private:
Open_HashTable<K, std::pair<K, V>, MapKOfT, Hash> _hashTable;
};
}

View File

@ -0,0 +1,70 @@
#pragma once
#include "HashTable.hpp"
namespace Lenyiin
{
template <class K, class Hash = _Hash<K>>
class Unordered_Set
{
public:
struct SetKOfT
{
const K& operator()(const K& key)
{
return key;
}
};
typedef typename Open_HashTable<K, K, SetKOfT, Hash>::iterator iterator;
iterator begin()
{
return _hashTable.begin();
}
iterator end()
{
return _hashTable.end();
}
// 构造函数: 指定哈希表的初始大小
Unordered_Set(size_t bucket_count = 10)
: _hashTable(bucket_count)
{
}
// 析构
~Unordered_Set()
{
}
// 插入元素
std::pair<iterator, bool> Insert(const K& key)
{
return _hashTable.Insert(key);
}
// 查找元素
bool Find(const K& key)
{
return _hashTable.Find(key);
}
// 删除元素
bool Erase(const K& key)
{
return _hashTable.Erase(key);
}
// 打印
void Print() const
{
_hashTable.Print();
}
private:
// 使用哈希表作为底层数据结构
Open_HashTable<K, K, SetKOfT, Hash> _hashTable;
};
}

View File

@ -0,0 +1 @@
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp;E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Windows_.6c808d8b\x64\Debug\Main.obj

View File

@ -0,0 +1,2 @@
PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.41.34120:TargetPlatformVersion=10.0.22000.0:
Debug|x64|E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\|

View File

@ -0,0 +1,2 @@
^E:\GIT 仓库\公开仓库\10_UNORDERED-SET-MAP\WINDOWS_UNORDERED_SET_MAP\WINDOWS_.6C808D8B\X64\DEBUG\MAIN.OBJ
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Windows_.6c808d8b\x64\Debug\Windows_Unordered_Set_Map.ilk

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<ProjectOutputs>
<ProjectOutput>
<FullPath>E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\x64\Debug\Windows_Unordered_Set_Map.exe</FullPath>
</ProjectOutput>
</ProjectOutputs>
<ContentFiles />
<SatelliteDlls />
<NonRecipeFileRefs />
</Project>

View File

@ -0,0 +1,38 @@
 Main.cpp
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(134,15): warning C4244: “参数”: 从“time_t”转换到“unsigned int”可能丢失数据
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(161,12): warning C4834: 放弃具有 [[nodiscard]] 属性的函数的返回值
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(169,11): warning C4834: 放弃具有 [[nodiscard]] 属性的函数的返回值
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(666,22): warning C4267: “初始化”: 从“size_t”转换到“int”可能丢失数据
(编译源文件“Main.cpp”)
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(666,22):
模板实例化上下文(最早的实例化上下文)为
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(215,85):
查看对正在编译的 类 模板 实例化“Lenyiin::Open_HashTable<int,int,Lenyiin::SetKeyOfT<int>,Lenyiin::_Hash<int>>”的引用
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(663,14):
在编译 类 模板 成员函数“void Lenyiin::Open_HashTable<int,int,Lenyiin::SetKeyOfT<int>,Lenyiin::_Hash<int>>::Print(void) const”时
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(230,13):
请参阅 "Test_Open_HashTable_1" 中对 "Lenyiin::Open_HashTable<int,int,Lenyiin::SetKeyOfT<int>,Lenyiin::_Hash<int>>::Print" 的第一个引用
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(281,22): warning C4267: “初始化”: 从“size_t”转换到“int”可能丢失数据
(编译源文件“Main.cpp”)
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(281,22):
模板实例化上下文(最早的实例化上下文)为
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(194,65):
查看对正在编译的 类 模板 实例化“Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>”的引用
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(279,14):
在编译 类 模板 成员函数“void Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::Print(void)”时
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(204,13):
请参阅 "Test_Close_HashTable" 中对 "Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::Print" 的第一个引用
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(58,20): warning C4101: “koft”: 未引用的局部变量
(编译源文件“Main.cpp”)
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(58,20):
模板实例化上下文(最早的实例化上下文)为
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(56,14):
在编译 类 模板 成员函数“void Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::CheckCapacity(void)”时
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\HashTable.hpp(143,26):
请参阅 "Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::Insert" 中对 "Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::CheckCapacity" 的第一个引用
E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\Main.cpp(195,14):
请参阅 "Test_Close_HashTable" 中对 "Lenyiin::Close_HashTable<int,int,Lenyiin::SetKeyOfT<int>>::Insert" 的第一个引用
Windows_Unordered_Set_Map.vcxproj -> E:\Git 仓库\公开仓库\10_unordered-set-map\Windows_Unordered_Set_Map\x64\Debug\Windows_Unordered_Set_Map.exe

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35312.102
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Windows_Unordered_Set_Map", "Windows_Unordered_Set_Map.vcxproj", "{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Debug|x64.ActiveCfg = Debug|x64
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Debug|x64.Build.0 = Debug|x64
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Debug|x86.ActiveCfg = Debug|Win32
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Debug|x86.Build.0 = Debug|Win32
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Release|x64.ActiveCfg = Release|x64
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Release|x64.Build.0 = Release|x64
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Release|x86.ActiveCfg = Release|Win32
{6C808D8B-0CB3-42B6-AF2E-ED6384CC59B8}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CDBD2AFD-9BDF-4941-A594-4E0BFAA55F03}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{6c808d8b-0cb3-42b6-af2e-ed6384cc59b8}</ProjectGuid>
<RootNamespace>WindowsUnorderedSetMap</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="HashTable.hpp" />
<ClInclude Include="Unordered_Map.hpp" />
<ClInclude Include="Unordered_Set.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="HashTable.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="Unordered_Set.hpp">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="Unordered_Map.hpp">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>