1.标准模板库(STL)主要包括三部分
1)容器:存储和管理对象的集合;
2)算法:以泛型(一般化的类型)的方式对容器中的元素进行计算对元素和容器的类型通用;
3)迭代器:在不暴露容器内部表示的前提下,访问其中的元素 联系容器和算法的纽带;
2.STL的所有组件都是模板,全面支持泛型操作。
3.STL的终极目标就是令数据结构和算法与类型无关。
4.STL的设计宗旨是在尽量小的框架内实现最大的弹性。
2.十大容器
1)线性容器:向量(vector)、列表(list)、双端队列(deque);
2)适配器容器:堆栈(stack)、队列(queue)、优先队列(priority_queue);
3)关联容器:映射(map)、多重映射(multimap)、集合(set)、多重集合(multiset);
3.容器的共同特征
1)所有的容器都支持完整意义上的拷贝构造和拷贝赋值,可以将容器作为一个整体,用于构造或赋值给另一个同类型的容器,前提是容器中的元素类型也要支持完整意义上的拷贝构造和拷贝赋值。
2)只用容器和元素的类型都相同的容器之间才能进行关系比较。
3)能够被放到容器中的元素都应该支持深拷贝语义,因此auto_ptr不适合被放入容器。
Student s (...);`
list<Student> ls;
ls.push_back (s);
4)所有的容器都支持四种迭代器:
iterator
const_iterator
reverse_iterator
const_reverse_iterator
只有以连续内存存放元素的容器才支持随机迭代:向量、双端队列以及以它们为底层容器的适配器容器。
二、向量(vector)
1.基本特性
1)连续内存,下标访问;
a[x] <==> *(a+x) : O(1);
2)动态内存管理
A.向量的内存空间会随着新元素的加入而自动增长。
B.内存空间的连续性不会妨碍向量元素的持续增加。
C.向量也支持预分配内存空间,减少动态内存分配的开销。
3)支持随机插入和删除,但是插入和删除的位置越靠近首端效率越低。
2.实例化
#include <vector>
1)vector<元素类型> 向量对象;
vector<int> vi; // 空向量
2)vector<元素类型> 向量对象 (初始大小);
vector<int> vi (10); // 初始大小10个int元素
基本类型:用0初始化。
类类型:用缺省构造函数初始化,如果元素类型没有缺省构造函数,该定义语句将导致编译错误。
3)vector<元素类型> 向量对象 (初始大小,元素初值);
vector<int> vi (10, 13); // 由10个13组成的向量
vector<Student> vs (10, Student ("张飞", 22));
4)vector<元素类型> 向量对象 (源容器的起始迭代器, 源容器的终止迭代器);
int a[5] = {1, 2, 3, 4, 5};
vector<int> vi (a, a+5); // 1 2 3 4 5
vector<int> vi (&a[0], &a[5]); // 1 2 3 4 5
vector<int> vi (&a[0], &a[4]); // 1 2 3 4
&*(a+5) <==> a+5
成员函数size()返回向量中的元素个数。
3.向量的迭代器支持随机迭代
和整数做+/-/+=/-=运算
迭代器之间可以做-/>/</>=/<=运算
4.关于迭代器的类型
迭代方向:正向/反向
访问方式:顺序/随机
只读特性:左值/右值
5.大小和容量
大小:实际容纳的元素个数
容量:最多能容纳的元素个数
大小<=容量
获取大小:size_type size (void) const;
改变大小:void resize (size_type num, const value_type& val = value_type ());
往小改,被裁掉的元素会析构,往大改,新增元素会构造,新元素的初始值取自第二个参数。
int i = int ();
double d = double ();
清空向量:void clear (void); <==> resize (0)
是否为空:bool empty (void) const;
获取容量:size_type capacity (void) const;
改变容量:void reserve (size_type capacity);
新增部分不初始化、不构造,仅仅分配内存。
一、双端队列(deque)
1.双端队列具有向量所有的功能,除了capacity()和reserve()以外。
2.在双端队列的头部与在其尾部做插入(insert)/删除(erase)的效率是对称的。
3.和向量相比,双端队列的内存开销略大一些,对元素做下标访问的效率也略低一些。
4.和向量相比,双端队列增加了向首部压入元素和从首部弹出元素的成员函数:push_front()、pop_front()。
二、列表(list)
1.列表是按照链式线性表(链表)的形式进行存储的。元素内存不连续,不支持下标运算符,不支持随机迭代。
2.在容器的任何位置进行插入和删除操作,其时间复杂度都是常数级的(O(1))。
3.列表内存空间的使用效率比向量和双端队列要高。
4.常用成员函数
front/push_front/pop_front
back/push_back/pop_back
insert/erase
size/resize/clear/emtpy
begin/end/rbegin/rend
1)删除所有匹配元素
void remove (const value_type& val);
2)将连续出现的重复元素唯一化
void unique (void);
10 20 20 20 30 20 20 40 20 50 -unique->
10 20 30 20 40 20 50
3)将一个链表的部分或全部剪切到另一个链表中
void splice (iterator pos, list& lst);
将参数链表lst的全部元素剪切到调用链表pos之前。
void splice (iterator pos, list& lst, iterator del);
将参数链表lst中del处的元素剪切到调用链表pos之前。
void splice (iterator pos, list& lst, iterator begin,
iterator end);
将参数链表lst中介于begin和end之间的元素剪切到调用链表pos之前。
以上三个剪切函数的时间复杂度都是常数级的。
4)排序
void sort (void);
用元素类型的“<”运算符比大小。
void sort (less cmp);
用参数比较器比大小。
5)合并
void merge (list& lst);
将有序的参数链表lst合并到有序的调用链表中,并保持结果依然有序。
void merge (list& lst, less cmp);
用参数比较器判断大小进行合并。
三、堆栈
堆栈接口 底层接口
push push_back
pop pop_back
top back
size size
empty empty
向量、双端队列、列表以及自定义任何支持上述底层接口的容器都可以用于适配堆栈。缺省底层容器是双端队列。
#include <stack>
stack<元素类型, 底层容器类型<底层容器元素类型> >
stack<int, vector<int> > si;
stack<string> ss;
四、队列(queue)
队列接口 底层接口
push push_back
pop pop_front
front front
back back
size size
empty empty
底层可以选择deque、list或其它自定义并提供以上底层接口的容器。缺省底层容器是deque。
五、优先队列(priority_queue)
1.每当向优先队列压入一个元素后,队列中具有最高优先级的元素被自动排列到队列的最前面。
2.利用元素类型的“<”运算符判断优先级,以大者为优。
#include <queue>
priority_queue<int> pi; // 缺省底层容器是deque
priority_queue<int, vector<int> > pi; // 可以用vector适配
3.利用小于比较器判断优先级,以大者为优。
priority_queue<int, vector<int>, Prioritize> ps;
4.可以用vector、deque或者自定义的支持随机迭代的容器作为priority_queue的底层容器,list因其只支持顺序迭代,故不能用于适配priority_queue。
六、映射(map)
1.映射是一个key-value对的序列,其中每个key都是唯一的。
2.影射中所有的key-value对,按key的升序排列,以平衡有序二叉树的形式存储。
3.映射可以根据key快速(对数时间)地找到与之对应的value。
4.利用key类型的“<”运算符比较大小。
map<string, int> si;
| |
key的类型 value的类型
5.利用针对key类型的小于比较器比较大小。
map<string, int, Less> si;
|
小于比较器的类型
6.映射支持以key为索引的下标运算符。
map<string, int> si;
si["张飞"] = 90;
如果下标运算符中的索引key不存在,映射就会自动建立一个新的key-value对,其中的key存入所给下标,同时返回与之对应的value的引用。如果下标运算符中的索引key已存在,直接返回与之对应的value的引用。
7.映射的基本访问单元为由key和value组合而成的pair。
template<typename KEY, typename VAL>
class pair {
public:
pair (KEY const& key, VAL const& val) :
first (key), second (val) {}
KEY first;
VAL second;
};
直接构造:pair<string, int> p ("张飞", 90);
工厂构造:make_pair ("张飞", 90);
template<typename KEY, typename VAL>
pair<KEY, VAL> make_pair (KEY const& key,
VAL const& val) {
return pair<KEY, VAL> (key, val);
}
8.常用成员函数
1)插入元素
pair<iterator, bool> insert (pair<KEY, VAL> const& pair);
2)删除元素
void erase (iterator pos);
删除指定位置的元素。
void erase (iterator begin, iterator end);
删除指定范围的元素。
size_type erase (KEY const& key);
删除与指定key相匹配的元素。
3)查找匹配元素
iterator find (KEY const& key);
查找成功返回与key相匹配的元素的迭代器,查找失败返回容器的终止迭代器。不需要key的类型支持“==”运算符,只要支持小于比较即可。
A 不< B
B 不< A
20
/ \
10 30 > 30 ?
七、多重映射(multimap)
1.允许key重复,表示一对多的映射关系。
B->张飞
B->赵云
B->刘备
2.不支持下标运算符。
3.常用成员函数
iterator lower_bound (KEY const& key);
返回与key匹配的元素下限,即第一个匹配key的元素的迭代器
iterator upper_bound (KEY const& key);
返回与key匹配的元素上限,即最后一个匹配key的元素的下一个位置的迭代器
A->曹操
B->张飞 <- 匹配下限
B->赵云
B->刘备
C->黄忠 <- 匹配上限
pair<iterator, iterator> equal_range (KEY const& key);
返回值pair的first是与key匹配的元素下限,second成员是与key匹配的元素上限。
八、集合(set)
1.集合就是没有value的映射。
2.构造形式
set<string> ss; // 用“<”比大小
set<string, Less> ss; // 用比较器比大小
九、多重集合(multiset)
1.既可以视为允许key重复的set,也可以视为没有value的multimap。
2.构造形式
multiset<string> ss; // 用“<”比大小
multiset<string, Less> ss; // 用比较器比大小
十、泛型算法
1.STL的泛型算法主要完成查找、排序、计数、合并、填充、比较、交换、删除以及划分等任务。
2.STL算法通常以迭代器作为参数,无需了解所操作容器的内部细节,因此STL算法又被称为泛型算法。
3.一个泛型算法能否被应用一种容器,完全取决于该容器能否提供算法所需要的迭代器。
4.STL算法中凡是和比较大小有关的操作,一般都会提供两种范式:小于运算符和比较器。