STL的容器能够分为下面几个大类:
一:序列容器, 有vector, list, deque, string.
二 : 关联容器, 有set, multiset, map, mulmap, hash_set, hash_map, hash_multiset, hash_multimap
三: 其它的杂项: stack, queue, valarray, bitset
容器类共享部分公共接口。
标准库定义的三种顺序容器类型:vector、list、deque(double-ended queue的缩写。发音为“deck”)。它们的区别仅在訪问元素的方式,以及加入或删除元素相关操作的代价。顺序容器适配器包含:stack、queue和priority_queue。容器仅仅定义了少量操作,大多数操作由算法库提供。
假设两个容器提供了同样的操作,则它们的接口(函数名和參数个数)应该同样。
标准容器类 | 说明 |
顺序性容器 | |
vector | 从后面高速的插入与删除,直接訪问不论什么元素 |
deque | 从前面或后面高速的插入与删除。直接訪问不论什么元素 |
list | 双链表,从不论什么地方高速插入与删除 |
关联容器 | |
set | 高速查找,不同意反复值 |
multiset | 高速查找,同意反复值 |
map | 一对多映射。基于keyword高速查找。不同意反复值 |
multimap | 一对多映射,基于keyword高速查找,同意反复值 |
容器适配器 | |
stack | 后进先出 |
queue | 先进先出 |
priority_queue | 最高优先级元素总是第一个出列 |
容器类型
vector | 容器,支持高速随机訪问(连续存储) |
list | 链表。支持高速插入/删除 |
deque | 双端队列,支持随机訪问(连续存储)。两端能高速插入和删除 |
stack | 栈 |
queue | 队列 |
priority_queue | 优先级队列 |
顺序容器的定义
顺序容器的构造和初始化
下表为迭代器为全部容器类型所提供的运算:
*iter | 返回类型iter所指向的元素的引用 |
iter->mem | 对iter进行解引用。并取得指定成员 |
++iter | 给iter加1。使其指向容器中下一个元素 |
iter++ | |
--iter | 给iter减1,使其指向容器中前一个元素 |
iter-- | |
iter1 == iter2 | 当两个迭代器指向同一个容器中的同一元素,或者当它们都指向 |
iter1 != iter2 | 同一个容器的超出末端的下一个位置时,两个迭代器相等。 |
vector和deque容器的迭代器提供了额外的运算:迭代器的算术运算和还有一些关系运算,例如以下表所看到的:
iter + n | 在迭代器上加(减)整数值,将产生指向容器中前面(后面)第n个元素的迭代器; |
iter - n | 新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置。 |
iter1 += iter2 | 复合运算:先加(减),再赋值 |
iter1 -= iter2 | |
iter1 - iter2 | 仅仅适用于vector和deque |
>, >=, <, <= | 比較迭代器的位置关系;仅仅适用于vector和deque |
关系操作符仅仅适用于vector和deque容器。这是由于仅仅有这两种容器为其元素提供高速、随机的訪问。
它们确保可依据元素位置直接有效地訪问指定的容器元素。这两种容器都支持通过元素位置实现的随机訪问,因此它们的迭代器能够有效地实现算术和关系运算。
迭代器范围:[first, last)是一个左闭合区间,表示范围从first開始,到last结束。但不包含last。
注意:假设first不等于last,则对first重复做自增运算必须可以到达last。否则,即last位于first之前,则将发生没有定义行为。
迭代器范围使用左闭合的意义:由于这样能够统一表示空集,就无需特别处理。
另外,使用迭代器时,要特别留意迭代器的可能的失效问题。
訪问元素
back() | 返回容器的最后一个元素的引用。假设容器为空,则该操作没有定义 |
front() | 返回容器的第一个元素的引用。假设容器为空,则该操作没有定义 |
c[n] |
返回下标为n的元素的引用;假设n<0 or n>=size(),则该操作没有定义 (注:仅仅适用于vector和deque容器) |
at[n] | 返回下标为n的元素的引用;假设下标无效。则抛出异常out_of_range异常
(注:仅仅适用于vector和deque容器) |
删除元素
erase(p) | 删除迭代器p所指向的元素。返回一个迭代器,它指向被删除的元素后面的元素。假设p指向容器内最后一个元素,则返回的迭代器指向容器的超出末端的下一个位置;假设p本身就是指向超出末端的下一个位置的迭代器,则该函数没有定义 |
erase(b, e) | 删除[b, e)内的全部元素。返回一个迭代器,它指向被删除元素段后面的元素。假设e本身就是指向超出末端的下一个位置的迭代器。则返回的迭代器也指向超出末端的下一个位置。 |
clear() | 删除容器内的全部元素,返回void |
pop_back() | 删除容器内的最后一个元素。返回void。假设容器为空。则该操作没有定义。 |
pop_front() | 删除容器内的第一个元素,返回void。 假设c为空容器,则该操作没有定义 (注:仅仅适用于list和deque容器) |
赋值与swap
c1 = c2 | 删除容器c1的全部元素,然后将c2的元素复制给c1。 c1和c2的类型必须同样。 |
c1.swap(c2) | 交换内容:调用该函数后,c1中存放的是c2原来的元素,c2中存放的是c1原来的元素。c1和c2的类型必须同样。该函数的运行速度通常要比将c2的元素拷贝到c1的操作快。 |
c.assign(b, e) |
又一次设置c的元素:将迭代器b和e标记的范围内全部的元素拷贝到c中。b和e必须不是指向c中元素的迭代器。 |
c.assign(n, t) | 将容器c又一次设置为存储n个值为t的元素。 |
注意:assign操作首先删除容器内全部的元素,再将參数所指定的新元素插入到容器中。
swap操作不会删除或插入不论什么元素。并且保证在常量时间内实现交换。
因为容器内没有移动不论什么元素。因此迭代器不会失效。但要注意这些迭代器指向了还有一个容器中的元素。
容器的选用:
vector和deque容器提供了对元素的高速訪问。但付出的代价是。在容器的任何位置插入或删除元素,比在容器尾部插入和删除的开销更大,由于要保证其连续存储。须要移动元素。list类型在不论什么位置都能高速插入和删除,由于不须要保证连续存储,但付出的代价是元素的随机訪问开销较大。
特征例如以下:
1)与vector容器一样。在deque容器的中间insert或erase元素效率比較低;
2)不同于vector容器,deque容器提供高效地在其首部实现insert和erase的操作。就像在尾部一样。
3)与vector容器一样而不同于list容器的是,deque容器支持对全部元素的随机訪问。
4)在deque容器首部或尾部删除元素则仅仅会使指向被删除元素的迭代器失效。在deque容器的不论什么其它位置的插入和删除操作将使指向该容器元素的全部迭代器都失效。
容器的比較:
vector (连续的空间存储,能够使用[]操作符)高速的訪问随机的元素,高速的在末尾插入元素。可是在序列中间岁间的插入。删除元素要慢。并且假设一開始分配的空间不够的话,有一个又一次分配更大空间,然后拷贝的性能开销。
deque (小片的连续。小片间用链表相连,实际上内部有一个map的指针。由于知道类型。所以还是能够使用[],仅仅是速度没有vector快)高速的訪问随机的元素。高速的在開始和末尾插入元素,随机的插入,删除元素要慢。空间的又一次分配要比vector快,又一次分配空间后,原有的元素不须要拷贝。对deque的排序操作,可将deque先拷贝到vector,排序后在复制回deque。
list (每一个元素间用链表相连)訪问随机元素不如vector快,随机的插入元素比vector快。对每一个元素分配空间。所以不存在空间不够,又一次分配的情况。
set:内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比較快的哦。
map :一对一的映射的结合,key不能反复。
stack :适配器。必须结合其它的容器使用。stl中默认的内部容器是deque。
先进后出。仅仅有一个出口。不同意遍历。
queue: 是受限制的deque,内部容器一般使用list较简单。
先进先出,不同意遍历。
vector<bool> 与bitset<> ,前面的能够动态改变长度。
priority_queue: 插入的元素就有优先级顺序,top出来的就是优先级最高的了
valarray 专门进行数值计算的。添加特殊的数学函数。
一些容器选使用方法则:
1)假设程序要求随机訪问元素,则应使用vector或deque容器;
2)假设程序必须在容器的中间位置插入或删除元素。则应採用list容器;
3)假设程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素。则应採用deque容器。
4)假设仅仅须要在读取输入时在容器的中间位置插入元素,然后须要随机訪问元素。则能够在输入时将元素读入到一个list容器中,然后对容器排序,再将排序后的list容器拷贝到vector容器中。
5)假设程序既须要随机訪问,又须要在容器的中间位置插入或删除元素。此时应当权衡哪种操作的影响较大,从而决定选择list容器还是vector或deque容器。
注:此时若选择使用vector或deque容器。能够考虑仅仅使用它们和list容器所共同拥有的操作。比方使用迭代器而不是下标,避免随机訪问元素等,这样在必要时,能够非常方便地将程序改写为使用list容器。
容器适配器
适配器(adaptor)是标准库中通用的概念,包含容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于还有一事物的行为的一种机制。容器适配器让一种已存在的容器类型採用还有一种不同的抽象类型的工作方式实现,仅仅是发生了接口转换而已。
标准库提供了三种顺序容器适配器:queue, priority_queue和stack。
全部适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器參数的构造函数将參数容器的副本作为其基础值。
默认的stack和queue都基于deque容器实现。而priority_queue则在vector容器上实现。在创建适配器时。通过将一个顺序容器指定为适配器的第二个类型參数。可覆盖其关联的基础容器类型。
比如:
stack<int, vector<int> > int_stack; // 此时,int-stack栈是基于vector实现
对于给定的适配器,其关联的容器必须满足一定的约束条件。stack适配器所关联的基本容器能够是随意一种顺序容器类型。由于这些容器类型都提供了push_back、pop_back和back操作。queue适配器要求其关联的基础容器必须提供pop_front操作,因此其不能建立在vector容器上;priority_queue适配器要求提供随机訪问功能,因此不能建立在list容器上。
两个同样类型的适配器能够做==, !=, <, >, <=, >=这些关系运算,仅仅要其基本元素类型支持==和<两个操作就可以。这与容器大小比較原则一致。
这与容器大小比較原则一致。
栈
s.empty() | 假设栈为这人,则true。否则返回false |
s.size() | 返回栈中元素的个数 |
s.pop() | 删除栈顶元素,但不返回其值 |
s.top() | 返回栈顶元素的值,但不删除该元素 |
s.push(item) | 在栈项压入新元素 |
队列和优先级队列
标准库队列使用了先进先出(FIFO)的存储和检索策略。进入队列的元素被放置在尾部。下一个被取出的元素则取自队列的首部。
priority_queue默认使用元素类型的 < 操作符来确定它们之间的优先级关系,用户也能够定义自己的优先级关系。
在优先级队列中。新元素被放置在比它优先级低的元素的前面。
q.empty() | 假设队列为空,则返回true。否则返回false |
q.size() | 返回队列中元素的个数 |
q.pop() | 删除队首元素,但不返回其值 |
q.front() | 返回队首元素的值,但不删除该元素
(注:该操作仅仅适用于队列) |
q.back() | 返回队尾元素的值。但不删除该元素
(注:该操作仅仅适用于队列) |
q.top() |
返回具有最高优先级的元素值,但不删除该元素 (注:该操作仅仅适用于优先级队列。 MSDN也为queue提供了该操作) |
q.push(item) | 对于queue,在队尾压入一个新元素;
对于priority_queue。在基于优先级的适当位置插入新元素 |
STL各个容器的实现:
(1) vector
内部数据结构:数组。
随机訪问每一个元素。所须要的时间为常量。
在末尾添加或删除元素所需时间与元素数目无关,在中间或开头添加或删除元素所需时间随元素数目呈线性变化。
可动态添加或降低元素,内存管理自己主动完毕。但程序猿能够使用reserve()成员函数来管理内存。
vector的迭代器在内存又一次分配时将失效(它所指向的元素在该操作的前后不再同样)。
当把超过capacity()-size()个元素插入vector中时,内存会又一次分配,全部的迭代器都将失效。否则,指向当前元素以后的不论什么元素的迭代器都将失效。当删除元素时,指向被删除元素以后的不论什么元素的迭代器都将失效。
(2)deque
内部数据结构:数组。
随机訪问每一个元素,所须要的时间为常量。
在开头和末尾添加元素所需时间与元素数目无关,在中间添加或删除元素所需时间随元素数目呈线性变化。
可动态添加或降低元素,内存管理自己主动完毕,不提供用于内存管理的成员函数。
添加不论什么元素都将使deque的迭代器失效。在deque的中间删除元素将使迭代器失效。
在deque的头或尾删除元素时,仅仅有指向该元素的迭代器失效。
(3)list
内部数据结构:双向环状链表。
不能随机訪问一个元素。
可双向遍历。
在开头、末尾和中间不论什么地方添加或删除元素所需时间都为常量。
可动态添加或降低元素,内存管理自己主动完毕。
添加不论什么元素都不会使迭代器失效。
删除元素时,除了指向当前被删除元素的迭代器外,其他迭代器都不会失效。
(4)slist
内部数据结构:单向链表。
不可双向遍历。仅仅能从前到后地遍历。
其他的特性同list相似。
(5)stack
适配器,它能够将随意类型的序列容器转换为一个堆栈。一般使用deque作为支持的序列容器。
元素仅仅能后进先出(LIFO)。
不能遍历整个stack。
(6)queue
适配器,它能够将随意类型的序列容器转换为一个队列,一般使用deque作为支持的序列容器。
元素仅仅能先进先出(FIFO)。
不能遍历整个queue。
(7)priority_queue
适配器,它能够将随意类型的序列容器转换为一个优先级队列。一般使用vector作为底层存储方式。
仅仅能訪问第一个元素,不能遍历整个priority_queue。
第一个元素始终是优先级最高的一个元素。
(8)set
键和值相等。
键唯一。
元素默认按升序排列。
假设迭代器所指向的元素被删除,则该迭代器失效。其他不论什么添加、删除元素的操作都不会使迭代器失效。
(9)multiset
键能够不唯一。
其他特点与set同样。
(10)hash_set
与set相比較,它里面的元素不一定是经过排序的,而是依照所用的hash函数分派的。它能提供更快的搜索速度(当然跟hash函数有关)。
其他特点与set同样。
(11)hash_multiset
键能够不唯一。
其他特点与hash_set同样。
(12)map
键唯一。
元素默认按键的升序排列。
假设迭代器所指向的元素被删除,则该迭代器失效。其他不论什么添加、删除元素的操作都不会使迭代器失效。
(13)multimap
键能够不唯一。
其他特点与map同样。
(14)hash_map
与map相比較,它里面的元素不一定是按键值排序的,而是依照所用的hash函数分派的。它能提供更快的搜索速度(当然也跟hash函数有关)。
其他特点与map同样。
(15)hash_multimap
键能够不唯一。
其他特点与hash_map同样。