HashTable
解决碰撞的方式:
1.线性探测
负载系数 = 元素数量 / 桶数量
如果某个位置空间不可用,则循序往下(尾端回到头部)查找可用空间
惰性删除:只标记删除记号,实际删除待表格重新整理时再进行
2.二次探测
如果H位置被占用,则依序尝试H+1, H+4, H+9, …
3.开链separate chaining:每个桶维护一个list
节点:
template <class Value>
struct __hashtable_node
{
__hashtable_node *next;
Value val;
};
迭代器
forward_iterator
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {
node *cur; //所指的节点
hashtable *ht; //指向容器
iterator& operator++() {
//如果不是list尾部,直接返回
const node* old = cur;
cur = cur->next;
//如果不是list尾部,直接返回
if (!cur) {
//否则返回下一个不为空的list头部
size_type bucket = ht->bkt_num(old->val);
while (!cur && ++bucket < ht->buckets.size())
cur = ht->buckets[bucket];
}
}
return *this;
}
//没有--操作
};
hashtable数据结构
//EqualKey:判断键值是否相同的方法
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc = alloc>
class hashtable {
private:
vector<node*, Alloc> buckets; //存放桶
size_type num_elements;
public:
size_type bucket_count();
};
选择质数个桶,当元素个数>篮子个数,进行rehash,选择篮子个数*2附近的质数
空间配置
node *new_node(const value_type &obj);
void delete_node(node *n);
//构造函数调用initialize_buckets
void initialize_buckets(size_type n)
{
//寻找合适的桶数量
//返回最接近n并大于n的质数
const size_type n_buckets = next_size(n);
buckets.reserve(n_buckets);
buckets.insert(buckets.end(), n_buckets, (node*)0);
num_elements = 0;
}
插入
pair<iterator, bool> insert_unique(const value_type& obj); //不允许插入重复
iterator insert_equal(const value_type &obj); //允许插入重复
void resize(size_type num_elements_hint)
{
const size_type old_n = buckets.size();
if (num_elements_hint > old_n) {
const size_type n = next_size(num_elements_hint);
if (n > old_n) {
//创建新的bucket vector
vector<node *, A> tmp(n, (node*) 0);
//将原来的数据拷贝到新的bucket vector中
__STL_TRY {
for (size_type bucket = 0; bucket < old_n; ++bucket) {
node *first = buckets[bucket];
while (first) {
size_type new_bucket = bkt_num(first->val, n);
//每次都插入到list头部
buckets[bucket] = first->next;
first->next = tmp[new_bucket];
tmp[new_bucket] = first;
first = buckets[bucket];
}
}
buckets.swap(tmp);
}
}
}
获取桶序号的方式:取余
size_type bkt_num_key(const key_type &key, size_t n) const
{
return hash(key) % n;
}
hash-function
对于数值类型(char, (unsigned)short, (unsigned)int, (unsigned)long),直接返回其值(转型为size_t)
如:
template<>
__STL_TEMPLATE_NULL struct hash<char>
{
size_t operator()(char x) const {return x;}
对于char*:
inline size_t __stl_hash_string(const char* s)
{
unsigned long h = 0;
for (; *s; ++s)
h = 5 * h + *s;
return size_t(h);
}
unordered_set/unordered_multiset
底层机制以hash table完成
unordered_set采用insert_unique进行插入,unordered_multiset采用insert_equal完成
unordered_map/unordered_multimap
底层机制以hash table完成
unordered_map采用insert_unique进行插入,unordered_multimap采用insert_equal完成