[BoolanC++微专业] Week6笔记

来源:互联网 发布:下载99宿舍客服软件 编辑:程序博客网 时间:2024/05/20 14:22

本周终于开始了STL的课程了,要知道入侯捷老师的坑还是从STL源码剖析这本书开始的。
一、STL组件:
STL是C++标准库的核心,也是依靠STL组成了一个泛型的程序库,可以大致分为以下几个方面:
(一)容器,用来管里某种的对象的集合。每一种容器有优点和缺点,所以STL为了应付不同的需求,STL准备了不同的容器类型。容器可以是array或者linked list,或者每个元素有一个特别的key。从这里能明显看出来C++和鸭子类型的语言有明显的区别,一个在运行效率上妥协,一个在开发效率上妥协。
(二)迭代器,用来在一个对象集合内遍历元素。这个对象集合或许是个容器,或者是容器的一部分。迭代器的好处是为各种各样的容器提供了一组很小的共同接口。迭代器的接口和指针差不多。
(三)算法,用来处理集合内的元素,它们可以出于不同的目的而查找、排序、修改、使用元素。通过迭代器的协助,我们只需要写一次算法,就可以将它应用于任意容器,因为所有迭代器的容器都是统一的接口。
(四)仿函数,又或叫做函数对象,是STL六大组件之一;仿函数虽然小,但却极大的拓展了算法的功能,几乎所有的算法都有仿函数版本。
(五)适配器,是用来修改其他组件接口的STL组件,是带有一个参数的类模板(这个参数是操作的值的数据类型)。STL定义了3种形式的适配器:容器适配器,迭代器适配器,函数适配器。
(六)STL的内存配置器在我们的实际应用中几乎不用涉及,但它却在STL的各种容器背后默默做了大量的工作,STL内存配置器为容器分配并管理内存。统一的内存管理使得STL库的可用性、可移植行、以及效率都有了很大的提升。SGI-STL的空间配置器有2种,一种仅仅对c语言的malloc和free进行了简单的封装,而另一个设计到小块内存的管理等,运用了内存池技术等。在SGI-STL中默认的空间配置器是第二级的配置器。SGI使用时std::alloc作为默认的配置器。
这里写图片描述
二、STL之容器:
(一)序列式容器,这是一种有序集合,其每个元素均有确凿的位置,取决于插入时机和地点,与元素值无关。如果以追加方式对一个集合置入6个元素,它们的排序次序将于置入次序一致。例如:array,vector,deque,list,forward-list等等。
(二)关联式容器,这是一种已排序集合,元素位置取决于其value或者key给定的某个排序准则。 例如:set,multiset,map,multimap。
(三)无序容器,这是一种无序集合,其内每个元素的位置无关紧要,唯一重要的是某特定元素是否位于此集合内。例如 :unordered_set,unordered_multiset,unordered_map和unordered_multimap 等等。

时间复杂度:
(1)时间频度 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
(2)时间复杂度 在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。 按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…, k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。 2、空间复杂度 与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作: S(n)=O(f(n)) 我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模。讨论方法与时间复杂度类似,不再赘述。

这里写图片描述

一般的容器是一个前开后闭的区间,比如:

vector<int> ivec{.....}ivec.begin()              //是一个迭代器,指向的是ivec的第一个元素。ivec.end()                //也是一个迭代器,但是其指向的ivec的最后一个元素后面的元素。*ivec.begin()             //可以得到第一个元素的值。*ivec.end()               //是一个错误。

各种容器有各种优点,也有各种缺点,我们可以根据uo 这些特点来选择容器:
array:
array是一个有序集合,允许允许随机访问,意思是可以在常量时间内直接访问任何元素,如果需要一个有个固定元素的序列,那么它会给你带来最佳的性能,因为内存被分配在stack中,绝不会被重新分配,而且你拥有随机反问能力。

vector:
vector将元素存到内部的dynamic array中,正如其名字所说,它是一个向量,所以它在末端附加或者删除元素的时候,vector的效率非常之好,但是如果在前端或者中端插入元素,效率就不怎么样了,因为作用点之后的每一个元素都必须移动一个位置,而每一次移动都得调用assignment操作符。

deque:
类似于vector,但是在 内部的实现上有非常大的不同,两者相比较,功能上的差异如下:
1、两端都能快速插入和删除元素,且都是常量时间内完成。
2、访问元素时多一个间接过程,所以元素访问和迭代器会慢一些。
3、迭代器需要在不同区块跳转,所以必须是一个智能指针而不是普通指针。
4、在内存区块大小有限制的系统中,deque可以内含更多元素,因为它使用不止一个区块的内存,所以deque的maxsize可以很大。
5、deque不支持对容量和内存重新分配时的控制,除了头尾两端,在 任何地点安插或者删除元素都将导致指向deque的任何指针,引用,迭代器失效,不过deque的内存分配优于vector。
6、deque会释放不再使用的内存快。

list:
list的内部结构完全和vector deque array不同,其内部使用的是一个双向链表。它自身提供了两个指针,或者称为锚点,用来指向第一个和最后一个元素。每个元素都有指针指向前一个和下一个元素。
和array、vector、deque的区别如下:
1、不支持随机反问。
2、任何位置上指向元素的安插和移除都很快。
3、安排和删除动作 不会使指向其他元素的各个指针、引用、迭代器失效。
4、list对于异常的处理方式是:要么成功,要么失败,不会只成功一半。

forward_list:
forward_list是一个行为受限的list,凡是list没有提供的功能,他也不支持。他的优点是内存用量较少,行动也略快速。

set和multiset:
这两个容器会根据特定的排序准则,自动将元素排序,不同在于set不允许元素重复,而multiset可以。
set和multiset通常由平衡二叉树完成,—这一点在standard中没有明确规定。
这两个容器不提供任何操作函数可以直接访问元素。
可以通过迭代器对元素进行间接访问,从迭代器的角度来看,元素值是常量。
由于使用二叉树,所以在查找时拥有对数复杂度。

map和multimap:
map和multimap将key/value当作元素进行管理。内部的数据结构也是平衡二叉树。map和multimap根据元素的key自动对元素进行排序,要修改元素的key必须先删除拥有该key的元素,然后插入拥有新的key/value的元素。

0 0
原创粉丝点击