(缺少代码实战之后补)
C++:关联性容器及底层实现
1关联性容器:
类似一个小型数据库
所用数据结构:
红黑树以及哈希表(散列表)
2红黑树(rb_tree)
2.1定义及性质(具体实现看数据结构红黑树)
- 是一个平衡二叉树搜索树。优势是排列规则有利查找插入,时间复杂度log(N),排列适度没有任一节点过深。
- 红黑树提供迭代器以及遍历操作。形成中序遍历进行查找搜索。
++iter; //获得排序状态,中序遍历的情况(左中右)
结果:g->d->h->b->a->e->c->f
- 不应该使用红黑树iter改变元素值(红黑树是二叉搜索树)。变成没有拒绝因为他为map和set服务,此时map的data可以改,但是用来排序的key不可以修改。
- rb_tree插入操作:
insert_unique();//key在整个数中独一无二,否则安插失败
insert_equal();//节点key可重复
2.2c++实现
- 红黑树是一个类模板,类目参数类型列表包括:key类型,key-data键值对类型,值类型、比较函数、alloc
- 红黑树成员包括:
size_type node_count; 红黑树容量
link_type header; //节点指针
Compare key_compare;//仿函数
3 set、multiset
- set和multiset以rb_tree为底层结构,有元素自动排序的特性,排序依据是key,因为set、multiset元素只有key。
- set/multiset提供遍历操作以及迭代器,按++iter遍历,就是排序状态。
- 无法使用iterator改变元素值,set/multiset底部是const iterator,禁止修改。
- set用的插入是insert_unique(),multiset使用的是insert_equal。
- set将红黑树做为基类,初始化时是对基类进行初始化(也类似适配器)
class set : public _Tree<_Tset_traits<_Kty, _Pr, _Alloc, false>>
using _Mybase = _Tree<_Tset_traits<_Kty, _Pr, _Alloc, false>>;
static const _Kty& _Kfn(const value_type& _Val) { //返回key类型是const类型
return _Val;
}
set() : _Mybase(key_compare()) {}
4map、multimap
- value里时key-data。key不可改,data可以改。
- 可以将key_type设为const
- map用的插入是insert_unique(),multimap使用的是insert_equal。
- 初始化与set类似。
template <class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = allocator<pair<const _Kty, _Ty>>>
class map : public _Tree<_Tmap_traits<_Kty, _Ty, _Pr, _Alloc, false>> {
using _Mybase = _Tree<_Tmap_traits<_Kty, _Ty, _Pr, _Alloc, false>>;
using value_type = pair<const _Kty, _Ty>; //将key设为const,data可以修改
- map的operator[]:下标查找实际是使用二分法实现lower_bound()查找,key不存在创建key到map里(没data存默认值)
5容器hashtable、散列表
5.1原理
- 假设对于某一种情况,有M种可能方式,设其中一种情况为T,则对于一个包含所有情况的列表,对应序号0~M-1,需要M*sizeof(T)的内存。
- 当容量足够时:一个萝卜一个坑,把值放在对应位置索引就行。
0 | 1 | 2 | … | … | … | M-1 |
---|
- 当容量不足时:需要进行缩减,假设需要将内存缩减至N大小,N<M*sizeof(T)。然后将T对象放在T对应序号K%N的位置。
0 | 1 | 2 | … | … | … | N-1 |
---|
5.2 哈希碰撞:
- 问题:可能存在对于不同序号,需要放在同一个索引的情况,叫做哈希碰撞。
解决:起初设计函数把后来的挪到其他位置。后来考虑算法复杂度的提升,故将同一个索引的值通过链表的形式串在一起(Separate Chaining)。
N =53,M = 200
- 再次问题:从前到后依次串起来,链表可能会很长,搜索很慢
解决:当链表过长时就打散开来(bucket)。增加二倍篮子,重新计算被链接的元素位置(不在列表中的个数)。
N:97,M:200
- 经验:
1.打散经验:如果被链接元素个数(不在列表中的个数)大于篮子个数,就危险,需要打散。此时rehashing,增加两倍篮子。
2.篮子数经验:一般篮子数会选为质数。
5.3代码实现
- 类模板参数:Value、Key、Hasher、ExtractKey、Keyeq、Alloc
Hasher:编号方法
ExtractKey:取key方法
Keyeq:key比较方法
- 迭代器设计
- hasher:对于自定义的类要做类模板特例化:
namespace std{
template<>
struct hash<Sales_data>{
typedef size_t result_type;
typedef Sales_data argument_type;
size_t operator()(const Sales_data& s)const{
return hash<string>()(s.bookNo)^
hash<unsigned>()(s.units_sold)^
hash<double>()(s.revenue);
}
};
}