hello~ 很高兴见到大家! 这次带来的是C++中关于哈希表这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢? 个 人 主 页: 默|笙
主要简单介绍一下unordered_set和set之间的差异,unordered_map和map跟它们差不多。 <unordered_set文档>

int firstUniqChar(string s) {
int count[26] = {0};
for (auto e : s)
{
count[e - 'a']++;
}
//i是用来遍历s的,不是用来遍历count的
for (int i = 0; i < s.size(); i++)
{
if (count[s[i] - 'a'] == 1)
{
return i;
}
}
return -1;
}当两个不同的key值经过哈希函数和模运算之后得到的数组下标可能一样,会映射到同一个位置去,这种问题我们叫做哈希冲突,也叫做哈希碰撞。
哈希函数是将任意大小的数据映射到固定范围的整数值的函数,这个整数值再通过某种方式(通常是取模)转换为数组下标。
key → 哈希函数 → 哈希值 → 取模运算 → 数组下标
假设哈希表中已经映射了存储了N个值,而哈希表的大小为M,那么负载因子 = N/M。负载因子越大,代表它的空间利用率越高,越小,代表它的存储效率越高。
在开放定址法中所有的元素都放到哈希表里,当一个关键字key用哈希函数计算出的位置冲突了,则按照某种规则找到一个没有存储数据的位置进行存储,开放定址法中负载因子一定是小于1的。这里的规则有三种:线性探测,二次探测,双重探测。

框架如下:
enum State{
EXIST,
DELETE,
EMPTY
};
template<class K, class V>
struct HashData
{
pair<K, V> _kv;
State _state = EMPTY;
};
template<class K, class V>
class HashTable
{
HashTable()
:_tables(11)
,_count(0)
{ }
private:
vector<HashData<K, V>> _tables;
};初次实现为,我们分解来看:
bool Insert(const pair<K, V>& kv)
{
//检查有没有重复的key
if (Find(kv.first))
{
return false;
}
//如果大于等于负载因子就要进行扩容
if ((double)_count / _tables.size() >= 0.7)
{
//设置一个新的哈希表,用来调用insert函数,实现insert的复用
HashTable newtables;
newtables._tables.resize(2 * _tables.size());
//重新映射一遍key值
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i]._state == EXIST)
{
newtables.Insert(_tables[i]._kv);
}
}
//交换两个表里的vector
_tables.swap(newtables._tables);
}
//通过映射位置找没有存储数据的位置
size_t hash0 = kv.first % _tables.size();
size_t hashi = hash0;
int i = 1;
while (_tables[hashi]._state != EMPTY)
{
hashi = (hash0 + i) % _tables.size();
i++;
}
_tables[hashi]._kv = kv;
_tables[hashi]._state = EXIST;
_count++;
return true;
}
HashData<K, V>* Find(const K& key)
{
size_t hash0 = key % _tables.size();
size_t hashi = hash0;
int i = 1;
while (_tables[hashi]._state != EMPTY)
{
if (_tables[hashi]._kv.first == key &&
_tables[hashi]._state == EXIST)
return &_tables[hashi];
hashi = (hash0 + i) % _tables.size();
i++;
}
return nullptr;
}

inline unsigned long __stl_next_prime(unsigned long n)
{
// Note: assumes long is at least 32 bits.
static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)K;
}
};
template<>
struct HashFunc<string>
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash += ch;
hash *= 131;
}
return hash;
}
};HashData<K, V>* Find(const K& key)
{
Hash hs;
size_t hash0 = hs(key) % _tables.size();
size_t hashi = hash0;
int i = 1;
while (_tables[hashi]._state != EMPTY)
{
if (_tables[hashi]._kv.first == key &&
_tables[hashi]._state == EXIST)
return &_tables[hashi];
hashi = (hash0 + i) % _tables.size();
i++;
}
return nullptr;
}
bool Erase(const K& key)
{
HashData<K, V>* ptr = Find(key);
if (ptr)
{
ptr->_state = DELETE;
--_count;
return true;
}
return false;
}#pragma
enum State{
EXIST,
DELETE,
EMPTY
};
template<class K>
struct HashFunc
{
size_t operator()(const K& key)
{
return (size_t)key;
}
};
template<>
struct HashFunc<string>
{
size_t operator()(const string& str)
{
size_t hash = 0;
for (auto ch : str)
{
hash += ch;
hash *= 131;
}
return hash;
}
};
template<class K, class V>
struct HashData
{
pair<K, V> _kv;
State _state = EMPTY;
};
template<class K, class V, class Hash = HashFunc<K>>
class HashTable
{
public:
HashTable()
:_tables(53)
,_count(0)
{ }
inline unsigned long __stl_next_prime(unsigned long n)
{
// Note: assumes long is at least 32 bits.
static const int __stl_num_primes = 28;
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473, 4294967291
};
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
bool Insert(const pair<K, V>& kv)
{
//检查有没有重复的key
if (Find(kv.first))
{
return false;
}
//如果大于等于负载因子就要进行扩容
if ((double)_count / _tables.size() >= 0.7)
{
//设置一个新的哈希表,用来调用insert函数,实现insert的复用
HashTable newtables;
newtables._tables.resize(__stl_next_prime(_tables.size() + 1));
//重新映射一遍key值
for (size_t i = 0; i < _tables.size(); i++)
{
if (_tables[i]._state == EXIST)
{
newtables.Insert(_tables[i]._kv);
}
}
//交换两个表里的vector
_tables.swap(newtables._tables);
}
//通过映射位置找没有存储数据的位置
Hash hs;
size_t hash0 = hs(kv.first) % _tables.size();
size_t hashi = hash0;
int i = 1;
while (_tables[hashi]._state != EMPTY)
{
hashi = (hash0 + i) % _tables.size();
i++;
}
_tables[hashi]._kv = kv;
_tables[hashi]._state = EXIST;
_count++;
return true;
}
HashData<K, V>* Find(const K& key)
{
Hash hs;
size_t hash0 = hs(key) % _tables.size();
size_t hashi = hash0;
int i = 1;
while (_tables[hashi]._state != EMPTY)
{
if (_tables[hashi]._kv.first == key &&
_tables[hashi]._state == EXIST)
return &_tables[hashi];
hashi = (hash0 + i) % _tables.size();
i++;
}
return nullptr;
}
bool Erase(const K& key)
{
HashData<K, V>* ptr = Find(key);
if (ptr)
{
ptr->_state = DELETE;
--_count;
return true;
}
return false;
}
private:
vector<HashData<K, V>> _tables;
size_t _count;
};今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~ 让我们共同努力, 一起走下去!