[Boolan] C++第八周 STL 泛型编程(二)
来源:互联网 发布:matlab数据归一化 编辑:程序博客网 时间:2024/06/08 12:57
一. deque
双端队列,支持快速随机访问,在头尾位置插入和删除很快
像上图,一般介绍deque是右上角这样的,两端都可以push和pop,并且可以像数组一样支持随机访问,一段连续的内存 其实他的实现并不是使用一整段连续内存来实现的,毕竟这样做,效率不高,而且扩展性不强 实际GUN的是使用多个大小相同的内存块,而不是一个连续的内存来实现的,并且使用一个类似索引的map来管理他们,并且在已有的内存块的用尽之后,如果继续向前向后添加元素时,是以一个内存块为单位进行申请的,然后索引添加到map中相应的位置 (上图中的map就是索引,map_size就是索引的大小) 一个deque的成员除了map,map_size外,还有两个迭代器,iterator start,iterator finish; start是指向第一个内存块首元素, finish是指向最后一个内存块尾元素的下一个元素 deque并且之所以可以像连续内存一样使用他,主要功劳是deque的iterator进行了大量的操作符重载,也包括随机访问的operator[]
1. iterator
继续看上图中的iterator部分,一个deque的iterator是由 cur, frist, last三个元素指针,一个指向索引的node指针组成前面说了,deque是使用索引加内存块实现的,它用来标识iterator位置的方式是 元素位于索引中那个内存块中的那个元素node就是当前内存块的在map的索引cur是元素在当前内存块的位置first是当前内存块首元素的地址last是当前内存块尾元素的下一个元素的地址通过了解的这些信息,我们来看下如何通过iterator插入一个元素,下列是GUN2.9中,insert的主干
iterator insert(iterator position, const value_type& x){ if(position.cur == start.cur) { //如果安插点是deque的最前端 push_front(x); return start; } else if (position.cur == finish.cur) { //如果是deque的尾端 push_back(x); iterator tmp = finish; //finish是尾端的下一个元素, --tmp; //所以,新插入的元素在finish前面 return tmp; } else { //如果在中间,在进行操作 return insert_aux(positon, x); }}
如果插入点是在deque的中间部分,需要考虑插入的点是距离首端近,还是距离尾端近,这样用来判断是移动待插入元素的前段的元素,还是尾端的元素,来给新插入的元素空出位置
template <class T, class Alloc, size_t BufSize>typename deque<T, Alloc, BufSize>::iteratordeque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x) { difference_type index = pos - start; //按插点之前的元素个数 value_type x_copy = x; if (index < size() / 2) { //如果安插点之前的元素个数少 push_front(front()); //把首元素向前拷贝一份 ... copy(fornt2, pos1, front1); //把安插位置到首端的元素向前移动一个 } else { //安插点之后的元素个数较少 push_back(back()); ... copy_backward(pos, back2, back1); } *pos = x_copy; //在安插点上放置要安插的元素 return pos;}
如何通过iterator模拟一段连续空间,最主要的通过 -, i++, i--,i+=4; i-=8; i[10]等操作来体现出来的,下面我们来说一下deque关于操作符的重载
//首先使用node相减 乘以buffer的大小,获得中间完整buffer有多少//然后在把cur前边的和last的后边的剪掉,得到的就是距离difference_type operator-(const self&x) const { return difference_type(buffer_size())*(node-x.node-1) + (cur-first) + (x.last - x.cur);}self& operator++() { ++cur; //切换下一元素 if (cur == last) { //如果++之后,超过了尾部元素 set_node(node+1); //那就后移一个node,然后cur指向当前的首元素 cur = first; } return *this;}self operator++(int) { self tmp = *this; ++*this; return tmp;}self& operator--() { if(cur == frist) { //如果已经是当前buffer的首元素了,就向前移动一个node set_node(node-1); cur = last; //并且指到尾元素 } --cur; return *this;}self operator--(int) { self tmp = *this; --*this; return tmp;}void set_node(map_pointer new_node) { node = new _node; first = *new_node; last = first + difference_type(buffer_size());}self& operator+=(difference_type n) { difference_type offset = n + (cur - first); if(offset >= 0 && offset < difference_type(buffer_size())) cur += n; //如果目标位置在同一buffer内 else { difference_ype node_offset = offset > 0 ? offset/difference_type(buffer_size()) : -difference_type((-offset-1)/buffer_size())-1; set_node(node + node_offset); //切换到正确的buffer cur = first + (offset-node_offset + difference_type(buffer_size())); } return *this;}self operator+(difference_type n) const { self tmp = *this; return tmp += n;}...
通过阅读源码;学习到应该尽量把单一功能的封成一个函数,并且如果能够通过已有函数实现的功能,就不要再重新写了,这也方便修改为了达到上面的目的,就要求再规划函数的时候考虑仔细,提前梳理那些函数,是可以复用的
queue stack
这两个容器都是不同程序的封装deque完成的
template <class T, Sequeue=deque<T>>class queue { ...protected: Sequence c; //底部容器 public: bool empty() const { return c.empty();} size_type size() { return c.size() }; reference front() {return c.front();} const_reference front() const { return c.front();} reference back() { return c.back();} const_reference back() { return c.back();} void push(const value_type &x) { c.push_back(x);} void pop() {c.pop_front();}}//并且可手动指定queue的底层容器queue<string, List<string>> c; //制定底层容器为链表
容器 rb_tree 红黑树
红黑树是平衡二元搜索树中常被使用的一种。 平衡二元搜索树的特点:排列规则有利于search和insert,并保持适度平衡--没有任何一个节点过深
template <class Key, class Value, // --> Key+Data = Value class KeyOfValue, //这个参数说明 怎么从value中获取key class Compare, //这个函数对象,用来比对Key的大小, class Allc = alloc> //分配器class rb_tree {protected: typedef __rb_tree_node<Value> rb_tree_node; ...public: typedef rb_tree_node *link_type; ...protected: //RB_tree 使用这三个成员变量可以完整表示 size_type node_count; //rb_tree 的大小 (节点的数量) link_type header; Compare key_compare; //key的大小比较准则; 应该是一个函数类};
创建一个红黑树需要5个参数 1. Key的类型2. Value的类型 是key和data的集合的类型3. KeyOfValue 是个仿函数,用来Value的类型中获取Key4. Compare 仿函数,用来比对Key的大小5. 分配器下面是用例
rb_tree<int, int, //这个 identity<int>, //这个仿函数是直接返回自己 less<int>, //比较Int的仿函数 alloc>myTree;//////////////////////rb_tree有两种插入节点的方式//不能插入重复的元素,如果插入重复的,不会报错,只是不起作用myTree.insert_unique(3); myTree.insert_unique(4);//可以插入重复的元素myTree.inser_equal(5);myTree.inser_equal(5);
容器 set,multiset
set/multiset 以rb_tre为底层结构,因此有元素自动排序的特性,排序的依据是key;set/multiset 元素的value和key合一;value就是key 就像上面的rb_tree的例子我们无法使用set/muliset的iterator改变元素,set的key不可以重复,是使用rb_tree的insert_unque()multiset元素的key可以重复,是使用rb_tree的insert_equal()下面是set的代码,可以看到set的迭代器是const的,所以不能改变
template <class Key, class Compare = less<Key>, class Alloc=alloc>class set { typedef Key key_type; typedef Key value_type; typedef Compare key_compare; typedef Compare value_compare;private: typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type; rep_type t;public: typedef typename rep_type::const_iterator iterator;};
map和multimap
map和multimap底层也是使用的rb_tree只是在value是一个pair的类型 例: make_pair<int, string> 这种就是一个Value然后Compare的仿函数是取的 make_pair<int, string>的key也就是int类型的值
tempalte <class Key, class T, class Compare = less<Key>, class Alloc = alloc>class map{public: typedef Key Key_type; typedef T data_type; typedef T mapped_type; typedef pair<const Key, T> value_type; typedef Compare key_compare;pirvate: typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type; rep_type t;public: typedef typename rep_type::iterator iterator;};
hashtable 容器
hashtable是一个存储的容器,存储的元素是散列的,没有规律的,查找非常迅速的容器存储的方式大致为: 1. 首先把这个元素进行hash,得到一个int/long类型的哈希值 2. 然后根据当前hashtable的大小,把元素的哈希值进行modulus计算,得到一个在当前hashtable范围内的值,并且把这个元素放到哪里 3. 如果两个元素之后modulus的值相同,就把元素往后排,使用链表来存储modulus相同的元素 4. 如果modulus相同的值过多,导致链表的长度超过hashtable,此时再查找一个元素的速度就会变慢,所以当链表的长度超过hashtable时,hashtable的长度会扩充(当前长度的两倍之后的第一个质数) 5. hashtable扩充之后,重新计算每个元素的modulus的值,然后放入hashtable 6. 每次hashtable扩充时的大小不是计算的,而是提起人为规定的,{53, 97, 193, 389, ...}
template<class Vaule, //Value = data+key 和rb_tree的Value一样 calss Key, //主键的类型 class HashFunc, //获取主键hash值的仿函数 class ExtractKey, //萃取的仿函数,从Value中获取Key class EquelKey, //判断Key是否相等的仿函数 class Alloc=alloc> class hashtable { public: typedef HashFunc hasher; typedef EquelKey ky_equal; typedef size_t size_type; private: hasher hash; key_equal equals; ExtractKey get_key; typedef __hashtable_node<Value> node; //hashtable上的node vector<node*, Alloc> buckets; //hashtable size_type num_elements; public: size_type bucket_count() const { return buckets.size(); } ... };
使用样例
hashtable<const char*, const char *, hash<const char *>, identity<const char *>, eqstr, alloc>ht(50, hash<const char*>(), eqstr()); //构造函数,制定hashtable的大小,hash仿函数类的对象实例, 对比两个key相等的仿函数类对象实例ht.insert_unique("111");ht.insert_unique("222");////struct eqstr{ bool operaotr() (const char *s1, const char *s1) const { return strcmp(s1, s2)==0; }};
下面是计算modulus的代码,最后是使用计算key的哈希值的int的hash值除以hashtable大小的余数来决定的
0 0
- [Boolan] C++第八周 STL 泛型编程(二)
- [Boolan] C++第七周 STL 泛型编程(一)
- [Boolan] C++第九周 STL 泛型编程(三)
- [Boolan] C++第十周 STL 泛型编程(四)
- Boolan STL与泛型编程 第二周笔记
- Boolan STL与泛型编程 第三周笔记
- Boolan STL与泛型编程 第五周笔记
- Boolan STL与泛型编程 第二周笔记
- Boolan C++ STL与泛型编程 第三周笔记
- (Boolan)STL与泛型编程作业一
- (Boolan) C++ STL与泛型编程
- (Boolan) C++ STL与泛型编程
- Boolan STL与泛型编程 第一周笔记
- Boolan STL与泛型编程 第四周笔记
- Boolan STL与泛型编程 第一周笔记
- Boolan C++ STL与泛型编程_1
- Boolan C++ STL与泛型编程_2
- Boolan C++ STL与泛型编程_3
- android中3种事件监听的实现方式
- 杭电 2026 首字母变大写
- 一.线程的初步了解和基本使用
- BlueTooth----蓝牙
- java 算法理解二
- [Boolan] C++第八周 STL 泛型编程(二)
- 继承System.Web.UI.Page的页面基类
- 初探Linux——Linux中常用的操作命令
- 如何在微信小程序中实现今日头条App那样的Topbar
- BIO
- getParameterValues和getParameter的区别
- [LeetCode]389. Find the Difference(找不同)
- react native开发:实现点击左侧选择项,右侧显示内容的功能
- java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonProcessingException