您的位置:首页 > 其它

STL学习笔记(一)

2016-09-25 00:00 148 查看

1. 容器(Containers)

容器分为两类:

序列式容器(Sequence containers),此乃可序群集,其中每个元素均有固定位置————取决于插入时机和地点,和元素值无关。STL提供三个定义好的序列式容器:vector,deque,list

关联式容器(Associative containers),此乃已序群集,元素位置取决于特定的排序准则,即元素位置取决于元素值,和插入次序无关。STL提供四个关联式容器:set,multiset,map,multimap

1.1 序列式容器

Vector
Vector将其元素置于一个dynamic array中加以管理,允许随机存取,即可以利用索引直接存取任何一个元素。
在array尾部附加或移除元素非常快速,但在array中部或头部安插元素比较费时

Deque
Deque与Vector一样,也是一个dynamic array,可以向两端发展,因此不论在尾部或头部安插元素都十分迅速,但在中间部分安插元素则比较费时

List
1. List由双向链表实作而成。
2. List不提供随机存取,即List并没有提供以operator[]直接存取元素的能力。
3. List的优势是:在任何位置上执行安插或删除动作都非常迅速。

序列式容器中,向容器首部或末端添加元素分别使用push_front和push_back函数,但vector中并未提供push_front函数

1.2 关联式容器

关联式容器依据特定的排序准则,自动为其元素排序。排序准则以函数形式呈现,用来比较元素值(value)或元素键(key)。缺省情况下以operator<进行比较,也可以提供自己的比较函数,定义不同的排序准则。

Set
Set的内部元素按其值自动排序,每个元素只能出现一次。

Multiset
Multiset和Set相同,只不过它允许元素重复。

Map
Map的元素都是“实值/键值”所形成的一个对组,每个元素都有一个键,是排序准则的基础。每一个键只能出现一次,不允许重复。Map可被视为关联式数组。

Multimap
Multimap和Map相同,但允许元素重复,即Multimap可包含多个键值相同的元素。Multimap可被当做“字典”

**所有关联式容器都有一个可供选择的template参数,指明排序准则。缺省采用operator<。

2. 容器适配器(Container Adapter)

除了上述容器类别,为满足特殊需求,STL提供了一些特别的并预先定义好的容器适配器,包括:

Stack
Stack容器对元素采取LIFO管理策略。

Queue
Queue容器对元素采取FIFO管理策略。

Priority Queue
Priority Queue容器中的元素可以拥有不同的优先级。

3. 迭代器

迭代器是一个“可遍历STL容器内全部或部分元素”的对象。一个迭代器用来指出容器中的一个特定位置。其基本操作如下:

operator*
返回当前位置上的元素值。如果该元素拥有成员,你可以透过迭代器,直接以operator->取用它们。

operator++、operator--
将迭代器前进或后退一个元素

operator==、operator!=
判断两个迭代器是否指向同一位置。

operator=
为迭代器赋值。

3.1 所有容器类别都提供一些成员函数,使我们可以获得迭代器####

begin()
返回指向容器起始点的迭代器。

end()
返回一个指向容器结束点的迭代器。结束点在最后一个元素之后,这样的迭代器称为“逾尾”迭代器。

rbegin()
返回一个指向最后一个元素的逆向迭代器

rend()
返回一个指向第一个元素前一个位置的逆向迭代器。

3.2 任何一种容器都定义有两种迭代器性别:

container::iterator
这种迭代器以“读写”模式遍历元素。

Container::const_iterator
这种迭代器以“只读”模式遍历元素。

3.3 迭代器分类

迭代器被划分为5类。STL预先定义好的所有容器中,其迭代器均属于一下两种类型:

双向迭代器(Bidirectional iterator)
双向迭代器可以双向行进:以++或--运算前进或后退。**list、set、multiset、map和multimap这些容器所提供的迭代器都属于此类。

随机存取迭代器(Random access iterator)
随机存取迭代器不但具备双向迭代器的所有属性,还具备随机访问能力。更明确的说,他们提供了“迭代器算术运算”必要的操作符(和“一般指针的算术运算符”完全对应)。你可以对迭代器增加或减少一个偏移量、处理迭代器之间的距离、或是使用<和>之类的retational(相对关系)操作符来比较两个迭代器。vector、deque和string所提供的迭代器都属于此类。

为了撰写尽可能与容器型别无关的范型程序代码,最好不要使用随机存取迭代器的特有操作。如下例,可以在任何容器上运作:

for (pos = col.begin();pos != col.end();++pos)
{
...
}

而下面的程序代码就不是所有容器都使用了:

for (pos = col.begin();pos < col.end();++pos)
{
...
}

因为只有random access iterator才支持operator<。

3.4 迭代器适配器

迭代器是一个纯粹抽象的概念:任何东西,只要其行为类似迭代器,它就是一个迭代器。因此,可以撰写一些类别,具备迭代器接口,但有着各不相同的行为。STL提供了数个预先定义的特殊适配器,即迭代器适配器:

Insert iterator(安插型迭代器)
安插型迭代器可以解决算法的“目标空间不足”问题。

Back inserter:内部调用push_back(),在容器尾端插入元素(追加)。

copy (col1.begin(),col1.end(),back_inserter(col2));

当然,只有在提供push_back()成员函数的容器中,back inserter才能派上用场。
2. Front inserter:内部调用push_front(),将元素安插于容器最前端。

copy (col1.begin(),col1.end(),front_inserter(col2));

只有在提供push_front()成员函数的容器中,front inserter才能派上用场,在STL中,这样的容器是deque和list。
3. General inserter:一般性的inserter,它的作用是将元素插入“初始化时接受之第二参数”所指定位置的前方,其内部调用insert()成员函数。所有STL容器都提供有insert()成员函数
|算是(expression)|Insert种类|
|-|-|
|back_inserter(container)|使用push_back()在容器尾端安插元素,元素排列次序和安插次序相同|
|front_inserter(container)|使用push_front()在容器前段安插元素,元素排列次序和安插次序相反|
|Insert(container,pos)|使用insert()在pos位置上安插元素,元素排列次序和安插次序相同|

Stream interator(流迭代器)

istream_iterator<string>(cin)
产生一个可从“标准输入流cin”中读取数据的stream iterator。元素通过一般的operator>>被读取进来

istream_iterator<string>()
调用istream iterator的default构造函数,产生一个代表“流结束符号”的迭代器。

ostream_iterator<string>(cout,"\n")
产生一个output stream iterator,通过operator<<向cout写入数据,第二个参数(可由可无)被用来作为元素之间的分隔符。

Reverse iterator(逆向迭代器)
所有容器都可以通过rbegin()和rend()成员函数产生reverse iterator。

4. 算法(Algorithm)

算法并非容器类别的成员函数,而是一种搭配迭代器使用的全局函数。

优势:所有算法只需实作一份,就可以对所有容器运作,数据与操作被明确划分开来,透过特定的接口彼此互动。

劣势:用法有失直观;某些数据结构和算法之间并不兼容,或者即使兼容也会导致糟糕的效能)。

4.1 区间

所有算法都用来处理一个或多个区间内的元素。

所有算法处理的都是半开区间————包含起始元素位置但不包含结尾元素位置。
[begin,end]


4.2 一些简单的算法

min_element()、max+element()
调用他们时,必须传入两个参数,定义出欲处理的元素范围。

sort()
将由两个参数设定出来的区间内的所有元素加以排序。还可以出入一个排序准则,缺省的是operator<。

find()
在给定范围内搜寻某个值,若成功返回目标元素,若失败,范围一个“逾尾迭代器”,亦即find()所接受的第二个参数。

reverse()
将区间内的元素反转。

distance()
返回两个迭代器之间的距离

4.3 处理多个区间

一些算法可以同时处理多个区间,通常必须设定第一个区间的起点和终点,至于其它区间,你只需设定起点即可,终点通常可由第一区间的元素数量推导出来。下面例子中,equal()从头开始逐一比较col1和col2的所有元素:

if (equal (col1.begin(),col1.end(),col2.begin()))
{
...
}

因此,col2中参与比较的元素数量,简洁取决于col1内的元素数量。
所以,如果某个算法用来处理多个区间,那么当你调用它时,务必确保第二区间所拥有的元素个数,至少和第一区间内的元素个数相同。
当然,如果目标区间空间不足,可以使用安插型迭代器

4.4 更易型算法(Manipulating algorithm)

更易型算法是指会“删除或重排或修改元素”的算法。

移除元素(remove)
算法remove()删除指定区间中的某一元素。

remove (col1.begin(),col1.end(),3);

但remove()算法并没有真正删除某一元素所占的空间,只是将之后的元素向前移动覆盖了某一数值。事实上,remove返回了一个新的终点,利用这个新的终点,通过容器所提供的成员函数,可以彻底删除一个元素。

col1.erase(remove(col1.begin(),col1.end(),3),col1.end());

为何算法不自己直接调用erase()?这就是STL为了获取灵活性而付出的代价。通过“以迭代器为接口”,STL将数据结构和算法分离开来。迭代器只是“容器中某一位置”的抽象概念而已,一般来说,迭代器对自己所属的容器一无所知,任何“以迭代器访问容器元素”的算法,都不得通过迭代器调用容器类别所提供的任何成员函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: