【STL】关联容器 — hashtable
template <class Value>
struct __hashtable_node
{
__hashtable_node* next;
Value val; // 存储实际值
};
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator { // 迭代器
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
hashtable;
.... typedef __hashtable_node<Value> node; // 定义迭代器对应类型
typedef forward_iterator_tag iterator_category; // 前向迭代器
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef Value& reference;
typedef Value* pointer; node* cur; // 迭代器眼下所指节点
hashtable* ht; // 和hashtable之间的纽带 __hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
__hashtable_iterator() {}
reference operator*() const { return cur->val; }
pointer operator->() const { return &(operator*()); }
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const { return cur == it.cur; }
bool operator!=(const iterator& it) const { return cur != it.cur; }
};
template <class V, class K, class HF, class ExK, class EqK, class A>
__hashtable_iterator<V, K, HF, ExK, EqK, A>&
__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++() // 注意类模板成员函数的定义
{
const node* old = cur;
cur = cur->next; // 移动到下一个node
if (!cur) { // 到了list结尾
size_type bucket = ht->bkt_num(old->val); // 依据节点值定位旧节点所在桶号
while (!cur && ++bucket < ht->buckets.size()) // 计算下一个可用桶号
cur = ht->buckets[bucket]; // 找到,另cur指向新桶的第一个node
}
return *this;
}
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey,
class Alloc>
class hashtable { // hash table数据结构
public:
typedef Key key_type;
typedef Value value_type;
typedef HashFcn hasher; // 散列函数类型
typedef EqualKey key_equal; typedef size_t size_type;
typedef ptrdiff_t difference_type;
.... private:
hasher hash; // 散列函数
key_equal equals; // 推断键值是否相等
ExtractKey get_key; // 从节点取出键值 typedef __hashtable_node<Value> node;
typedef simple_alloc<node, Alloc> node_allocator; // 空间配置器 vector<node*,Alloc> buckets; // 桶的集合,能够看出一个桶实值上是一个node*
size_type num_elements; // node个数
....
}
static const int __stl_num_primes = 28; // 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
};
// 插入新元素。键值不能反复
pair<iterator, bool> insert_unique(const value_type& obj)
{
resize(num_elements + 1); // 推断vector是否须要扩充
return insert_unique_noresize(obj); // 直接插入obj
}
template <class V, class K, class HF, class Ex, class Eq, class A>
void hashtable<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint) // 推断是否须要扩充vector
{
const size_type old_n = buckets.size();
if (num_elements_hint > old_n)
{ // 元素个数大于vector容量,则须要扩充vector
const size_type n = next_size(num_elements_hint);
if (n > old_n)
{
vector<node*, A> tmp(n, (node*) 0); // 建立一个暂时的vector作为转移目的地
for (size_type bucket = 0; bucket < old_n; ++bucket)
{ // 一个桶一个桶进行转移
node* first = buckets[bucket];
while (first)
{ // 一个节点一个节点进行转移
size_type new_bucket = bkt_num(first->val, n); // 散列过程,对n取模
buckets[bucket] = first->next;
first->next = tmp[new_bucket]; // 这一句和下一句表示从链表前端插入
tmp[new_bucket] = first;
first = buckets[bucket]; // first指向旧vector的下一个node
}
buckets.swap(tmp); // 两个vector的内容互换。使buckets彻底改变
}
}
}
}
- 扩充利用next_size函数。next_size的作用就是从质数表中选取最接近而且不小于num_elements_hint的质数并返回,利用这个较大值开辟一个新vector。
- 移动实质上就是指针的移动。又一次对每一个节点进行散列,然后从前链入到新的vector中。
- 交换过程就是上面代码红色部分。这里使用了vector内部的swap成员函数,将*this和tmp的内容进行了互换。这是copy-and-swap技术。《Effective C++》条款11有说明这个技术。扩充完vector后,就能够顺利插入须要插入的元素了。
template <class V, class K, class HF, class Ex, class Eq, class A>
pair<typename hashtable<V, K, HF, Ex, Eq, A>::iterator, bool> // 注意,返回一个pair
hashtable<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const value_type& obj) // 直接插入节点,无需扩充
{
const size_type n = bkt_num(obj); // 对obj进行散列,然后模上vector大小,从而确定桶号
node* first = buckets[n]; // first指向相应桶的第一个node for (node* cur = first; cur; cur = cur->next)
if (equals(get_key(cur->val), get_key(obj))) // 遇到同样node。则直接返回这个node
return pair<iterator, bool>(iterator(cur, this), false); // 没有遇到同样node,则在list开头插入
node* tmp = new_node(obj);
tmp->next = first;
buckets[n] = tmp;
++num_elements;
return pair<iterator, bool>(iterator(tmp, this), true);
}
參考:
版权声明:本文博客原创文章,博客,未经同意,不得转载。
【STL】关联容器 — hashtable的更多相关文章
- STL关联容器
这里简单学习一下STL关联容器,主要是map.multimap.set.multiset以及unordered_map.前四个底层实现都是利用红黑树实现的,查找算法时间复杂度为\(O(log(n))\ ...
- STL关联容器的基本操作
关联容器 map,set map map是一种关联式容器包含 键/值 key/value 相当于python中的字典不允许有重复的keymap 无重复,有序 Map是STL的一个关联容器,它提供一对一 ...
- STL关联容器总结
有序的都不带unordered,即如下: set multiset map multimap 其中带multi的表示关键字可以重复 无序的带unordered,如下: unordered_map un ...
- STL关联容器值hashtable
hashtable(散列表)是一种数据结构,在元素的插入,删除,搜索操作上具有常数平均时间复杂度O(1); hashtable名词 散列函数:负责将某一元素映射为索引. 碰撞(collision):不 ...
- STL 笔记(二) 关联容器 map、set、multimap 和 multimap
STL 关联容器简单介绍 关联容器即 key-value 键值对容器,依靠 key 来存储和读取元素. 在 STL 中,有四种关联容器,各自是: map 键值对 key-value 存储,key 不可 ...
- STL List容器
转载http://www.cnblogs.com/fangyukuan/archive/2010/09/21/1832364.html 各个容器有很多的相似性.先学好一个,其它的就好办了.先从基础开始 ...
- STL——关联式容器
一.关联式容器 标准的STL关联式容器分为set(集合)/map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和 multimap(多键映射表).这些容器的底层机制均以RB-tre ...
- STL之关联容器的映射底层
STL的关联容器有set, map, multiset, multimap.用于实现它们的底层容器有划入标准的rb_tree和待增加标准的hashtable. 底层容器rb_tree为上层容器提供了一 ...
- 《STL源码剖析》——第五、六:关联容器与算法
第五章.关联容器 5.0.关联容器 标准的STL关联式容器分为set(集合)和map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和multimap(多键映射表).这些容器的底层 ...
随机推荐
- 组合搜索(combinatorial search)在算法求解中的应用
1. 分治.动态规划的局限性 没有合适的分割方式时,就不能使用分治法: 没有合适的子问题或占用内存空间太大时,就不能用动态规划: 此时还需要回到最基本的穷举搜索算法. 穷举搜索(exhaustive ...
- squeeze() 函数——MATLAB
B=squeeze(A) 移除张量A的单一维,即返回和矩阵A元素相同,但所有单一维都移除的矩阵B,单一维是满足size(A,dim)=1的维. squeeze命令对二维数组是不起作用的; 如果A是一行 ...
- 【p092】分数线划定
Time Limit: 1 second Memory Limit: 128 MB [问题描述] 世博会志愿者的选拔工作正在A市如火如荼地进行.为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔 ...
- Swift之动画总结
UIView动画个人笔记,代码简单,不过多赘述.1.定义三个View @IBOutlet weak var mFirstView: UIView! @IBOutlet weak var mSecond ...
- Kinect for Xbox one(v2) + Ubuntu 14.04 +ROS 安装
相比于kinect for xbox 360(v1)通过结构光来获取深度,Kinect for Xbox one(v2) 采用time flight技术,极大改善了深度图像的性能. kinect fo ...
- 【solr基础教程之九】客户端 分类: H4_SOLR/LUCENCE 2014-07-30 15:28 904人阅读 评论(0) 收藏
一.Java Script 1.由于Solr本身可以返回Json格式的结果,而JavaScript对于处理Json数据具有天然的优势,因此使用JavaScript实现Solr客户端是一个很好的选择. ...
- 【34.88%】【codeforces 569C】Primes or Palindromes?
time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- CentOS7下安装Mysql失败经历--CentOS7使用yum安装和卸载Mysql过程
起因 自己租用的BandwagonVPS上安装了个CentOS7,然后开始安装各种软件,结果yum安装MySQL发现MySQL在yum源中的Mysql不对劲,于是自己百度搜索安装方法. 终于我搜到了这 ...
- 辛星浅析yaf框架中的类的自己主动载入问题
因为公司非常多项目都是基于yaf的,而非常多刚接触yaf的朋友问的第一个问题就是:yaf的自己主动载入是依照什么规则来的. 鉴于此.于是我特别开了一篇博文来记录一下. 首先在yaf中.models文件 ...
- [NPM] Run a set of similar npm scripts with a wildcard
In this lesson we will run a set of scripts that are grouped together with a wildcard using the npm- ...