STL = Standard Template Library,标准模板库,它是一个容器模板、迭代器模板、函数对象模板和算法模板的集合。它们的设计都是基于泛型编程原则的。STL容器是一个与数组类似的单元,可以存储若干个值;算法是完成特定任务的处方,算法通过使用模板,从而独立于所存储的对象的类型;通过使用迭代器接口,从而独立于容器的类型。迭代器是广义指针。
1.1、容器类型是可用于创建具体容器对象的模板。
1.2、STL的容器可以分为以下几个大类:
(1)序列容器: 有vector、list、 deque、string、forward_list、array(也被归为序列容器,虽然并不满足所有要求)。
(2)关联容器: 有set、 multiset、 map、 mulmap、hash_set、 hash_map、hash_multiset、 hash_multimap。(对容器概念的改进,将值与键关联在一起,并使用键来查找值。
(3)容器配接器:Stack、Queue、Priority Queue
1.3、STL各个容器的实现:
(1)、Vector
头文件:#inlcude
内部数据结构:数组。
特点:
① 随机访问每个元素,所需要的时间为常量。
② 在末尾增加或删除元素所需时间与元素数目无关,在中间或开头增加或删除元素所需时间随元素数目呈线性变化。
③ 可动态增加或减少元素,内存管理自动完成,但程序员可以使用reserve()成员函数来管理内存。
④ vector的迭代器在内存重新分配时将失效(它所指向的元素在该操作的前后不再相同)。当把超过capacity()-size()个元素插入vector中时,内存会重新分配,所有的迭代器都将失效;否则,指向当前元素以后的任何元素的迭代器都将失效。当删除元素时,指向被删除元素以后的任何元素的迭代器都将失效。
常用函数:
c.assign(beg,end) c.assign(n,elem) //将(beg; end)区间中的数据赋值给c。将n个elem的拷贝赋值给c。
c.at(idx) //传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back() //传回最后一个数据,不检查这个数据是否存在。
c.begin() //传回迭代器中的第一个数据地址。
c.capacity() //返回容器中数据个数。
c.clear() //移除容器中所有数据。
c.empty() //判断容器是否为空。
c.end() //指向迭代器中末端元素的下一个,指向一个不存在元素。
c.erase(pos) //删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end) //删除[beg,end)区间的数据,传回下一个数据的位置。
c.front() //传回第一个数据。
get_allocator //使用构造函数返回一个拷贝。
c.insert(pos,elem) //在pos位置插入一个elem拷贝,传回新数据位置
c.insert(pos,n,elem) //在pos位置插入n个elem数据,无返回值
c.insert(pos,beg,end) //在pos位置插入在[beg,end)区间的数据。无返回值
c.max_size() //返回容器中最大数据的数量。
c.pop_back() //删除最后一个数据。
c.pop_front() //删除头部数据。
c.push_back(elem) //在尾部加入一个数据。
c.rbegin() //传回一个逆向队列的第一个数据。
c.rend() //传回一个逆向队列的最后一个数据的下一个位置。
c.resize(num) //重新指定队列的长度。
c.reserve() //保留适当的容量。
c.size() //返回容器中实际数据的个数。
c1.swap(c2) //将c1和c2元素互换
(2)、Deque
头文件:#include
内部数据结构:双端队列
特点:
① 随机访问每个元素,所需要的时间为常量。
② 在开头和末尾增加元素所需时间与元素数目无关,在中间增加或删除元素所需时间随元素数目呈线性变化。
③ 可动态增加或减少元素,内存管理自动完成,不提供用于内存管理的成员函数。
④ 增加任何元素都将使deque的迭代器失效。在deque的中间删除元素将使迭代器失效。在deque的头或尾删除元素时,只有指向该元素的迭代器失效。
常用函数:与Vector相同
(3)、List
头文件:#include
内部数据结构:双向环状链表
特点:
① 不能随机访问一个元素。
②可双向遍历。
③在开头、末尾和中间任何地方增加或删除元素所需时间都为常量。
④可动态增加或减少元素,内存管理自动完成。
⑤增加任何元素都不会使迭代器失效。删除元素时,除了指向当前被删除元素的迭代器外,其它迭代器都不会失效。
常用函数:与Vector相同
(4)、Slist
头文件:#include
内部数据结构:单向链表
特点:
① 不可双向遍历,只能从前到后地遍历。
②其它的特性同list相似。
常用函数:
* void swap(slist&);
* iterator insert_after(iterator pos,const T& x); // 注意与前面不同的是,这里是在POS位置之后进行插入
* // vector、deque、list都是POS前插入
* iterator insert(iterator pos,const T& X); // 找到pos的前驱,再调用insert_after进行插入(pos前插入)
* void pop_front();
* iterator erase(iterator pos);
* iterator erase(iterator first,iterator last); // 注意是半闭区间[first,last)
* void clear();
* void remove(const T& x); // 删除所有等于value的元素
* void splice(iterator pos,slist& x); //将源list的内容部分或全部元素删除,拼插入到目的list。
* void splice(iterator pos,iterator i);
* void merge(slist& x); //合并链表
* void sort(); // 按"<"关系进行排序
* void unique(); // 删除重复元素,仅保留一个
(5)、Stack
头文件:#inlcude
内部数据结构:它可以将任意类型的序列容器转换为一个堆栈,一般使用deque作为支持的序列容器。
特点:
①元素只能后进先出(LIFO)。
②不能遍历整个stack。
常用函数:
bool empty()const;
size_type size();
T &pop();
void push(const T &x);
void pop();
(6)、Queue
头文件:#inlcude
内部数据结构:它可以将任意类型的序列容器转换为一个队列,一般使用deque作为支持的序列容器。
特点:
①元素只能先进先出(FIFO)。
②不能遍历整个queue。
常用函数:
bool empty()const;
size_type size();
T &front();
T &back();
void push(const T &x);
void pop();
(7)、Priority_Queue
头文件:#inlcude
内部数据结构:它可以将任意类型的序列容器转换为一个优先队列,一般使用vector作为底层存储结构.
特点:
①适配器,它可以将任意类型的序列容器转换为一个优先级队列,一般使用vector作为底层存储方式。
②只能访问第一个元素,不能遍历整个priority_queue。
③第一个元素始终是优先级最高的一个元素。
(8)、Set
头文件:#inlcude
内部数据结构:二叉树
特点:
①键和值相等。
②键唯一。
③元素默认按升序排列。
④如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。
(9)、Multiset
头文件:#inlcude
内部数据结构:二叉树
特点:
①键可以不唯一。
②其它特点与set相同。
(10)、Map
头文件:#inlcude
内部数据结构:二叉树。
特点:
①键唯一。
②元素默认按键的升序排列。
③如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。
(11)、Multimap
头文件:#inlcude
内部数据结构:二叉树。
特点:
①键可以不唯一。
②其它特点与map相同。
注意:关联容器支持通过键来高效地查找和读取元素,两个基本的关联容器是map和set。map的元素是“键-值”对的二元组形式:键用作元素在map中的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效地支持关于某个键是否存在的查询。set和map类型的对象所包含的元素都具有不同的键。如果需要一个键对应多个实例,则需要使用multimap或multiset类型。这两种类型允许多个元素拥有相同的键。
(12)、Hash_Set
头文件:#inlcude
内部数据结构:哈希表。
特点:
① 与set相比较,它里面的元素不一定是经过排序的,而是按照所用的hash函数分派的,它能提供更快的搜索速度(当然跟hash函数有关)。
②其它特点与set相同。
(13)、Hash_Multiset
头文件:#inlcude
内部数据结构:哈希表。
特点:
①键可以不唯一。
②其它特点与hash_set相同。
(14)、Hash_Map 头文件:#inlcude
内部数据结构:哈希表。
特点:
①与map相比较,它里面的元素不一定是按键值排序的,而是按照所用的hash函数分派的,它能提供更快的搜索速度(当然也跟hash函数有关)。
②其它特点与map相同。
(15)、Hash_Multimap
头文件:#inlcude <hash_map>
内部数据结构:哈希表。
特点:
①键可以不唯一。
②其它特点与hash_map相同。
注意:带有hash字样的关联容器,内部结构是哈希表,其它特性不变。hashtable 可以提供任何有名项的存取操作和删除操作,由于操作对象是有名项,故可被视为一种字典结构。用意是在常数时间内提供基本操作。常见的,我们可以把int存到相应值的数组里面,就可以通过O(1)的时间存取和删除。主要思想是通过hash函数,把对象映射到一个较小的容器里面,并且保证时间复杂度。映射到较小容器很可能出现碰撞问题,解决的方法常见的有:线性探测,二次探测,开链法。STL利用vector来当容器,采用开链法来解决冲突,从而实现hashtable。hashtable只能处理char,int,short等类型,不能处理string,double,float类型,想要处理的话必须自己加hash function.
2.1、基本概念:
迭代器(iterator)是连接容器和算法的纽带,为数据提供了抽象,使写算法的人不必关心各种数据结构的细节。迭代器提供了数据访问的标准模型——对象序列,使对容器更广泛的访问操作成为可能。
泛型编程的关键所在,就是如何找到一种通用的方法,来访问具有不同结构的各种容器中的每个元素,而这正是迭代器的功能。
迭代器是一种广义的指针,是指向序列元素指针概念的一种抽象。迭代器可以指向容器中的任意元素,还能遍历整个容器。
(序列)容器是数组的抽象,而迭代器则是指向数组指针的抽象。迭代器虽然是广义的指针,但是,迭代器并不是通用的指针。不同的容器可能需要不同的迭代器,实际上,在STL中,为每种容器都typedef了一个迭代器,名为iterator。例如,vector
2.2、类型:
(1) 输入迭代器(input iterator)——用于读取容器中的信息,但不一定能够修改它。
① 输入迭代器iter通过解除引用(即*iter),来读取容器中其所指向元素之值;
② 为了使输入迭代器能够访问容器中的所有元素的值,必须使其支持(前/后缀格式的)++ 操作符;
③ 输入迭代器不能保证第二次遍历容器时,顺序不变;也不能保证其递增后,先前指向的值不变。即,基于输入迭代器的任何算法,都应该是单通(single-pass)的,不依赖于前一次遍历时的值,也不依赖于本次遍历中前面的值。
可见输入迭代器是一种单向的只读迭代器,可以递增但是不能递减,而且只能读不能写。适用于单通只读型算法。
(2) 输出迭代器(output iterator)——用于将信息传输给容器(修改容器中元素的值),但是不能读取。例如,显示器就是只能写不能读的设备,可用输出容器来表示它。也支持解除引用和++操作,也是单通的。所以,输出迭代器适用于单通只写型算法。
(3)前向迭代器(forward iterator正向迭代器)——只能使用++操作符来单向遍历容器(不能用--)。与I/O迭代器一样,前向迭代器也支持解除引用与++操作。与I/O迭代器不同的是,前向迭代器是多通的(multi-pass)。即,它总是以同样的顺序来遍历容器,而且迭代器递增后,仍然可以通过解除保存的迭代器引用,来获得同样的值。另外,前向迭代器既可以是读写型的,也可以是只读的。
(4)双向迭代器(bidirectional iterator)——可以用++和--操作符来双向遍历容器。其他与前向迭代器一样,也支持解除引用、也是多通的、也是可读写或只读的。
(5)随机访问迭代器(random access iterator)——可直接访问容器中的任意一个元素的双向迭代器。
可见,这5种迭代器形成了一个层次结构:I/O迭代器(都可++遍历,但是前者只读/后者只写)最基本、前向迭代器可读写但只能++遍历、双向迭代器也可读写但能++/--双向遍历、随机迭代器除了能够双向遍历外还能够随机访问。
编译器无法检测出所传递的迭代器是一个无效形式的迭代器,当然也无法给出算法函数错误的提示,因为迭代器并不是真实的类别,它只是传递给函数模板的一种参数格式而已。
3.2 STL中算法分类:
修改性序列操作(27个) |
||
复制 |
copy() |
从序列的第一个元素起进行复制 |
copy_backward() |
从序列的最后一个元素起进行复制 |
|
交换 |
swap() |
交换两个元素 |
swap_ranges() |
交换指定范围的元素 |
|
iter_swap() |
交换由迭代器所指的两个元素 |
|
变换 |
transform() |
将某操作应用于指定范围的每个元素 |
替换 |
replace() |
用一个给定值替换一些值 |
replace_if() |
替换满足谓词的一些元素 |
|
replace_copy() |
复制序列时用一给定值替换元素 |
|
replace_copy_if() |
复制序列时替换满足谓词的元素 |
|
填充 |
fill() |
用一给定值取代所有元素 |
fill_n() |
用一给定值取代前n个元素 |
|
生成 |
generate() |
用一操作的结果取代所有元素 |
generate_n() |
用一操作的结果取代前n个元素 |
|
删除 |
remove() |
删除具有给定值的元素 |
remove_if() |
删除满足谓词的元素 |
|
remove_copy() |
复制序列时删除具有给定值的元素 |
|
remove_copy_if() |
复制序列时删除满足谓词的元素 |
|
唯一 |
unique() |
删除相邻的重复元素 |
unique_copy() |
复制序列时删除相邻的重复元素 |
|
反转 |
reverse() |
反转元素的次序 |
reverse_copy() |
复制序列时反转元素的次序 |
|
环移 |
rotate() |
循环移动元素 |
rotate_copy() |
复制序列时循环移动元素 |
|
随机 |
random_shuffle() |
采用均匀分布来随机移动元素 |
划分 |
partition() |
将满足某谓词的元素都放到前面 |
stable_partition() |
将满足某谓词的元素都放到前面并维持原顺序 |
序列排序及相关操作(27个) |
||
排序 |
sort() |
以很好的平均效率排序 |
stable_sort() |
排序,并维持相同元素的原有顺序 |
|
partial_sort() |
将序列的前一部分排好序 |
|
partial_sort_copy() |
复制的同时将序列的前一部分排好序 |
|
第n个元素 |
nth_element() |
将第n各元素放到它的正确位置 |
二分检索 |
lower_bound() |
找到大于等于某值的第一次出现 |
upper_bound() |
找到大于某值的第一次出现 |
|
equal_range() |
找到(在不破坏顺序的前提下)可插入给定值的最大范围 |
|
binary_search() |
在有序序列中确定给定元素是否存在 |
|
归并 |
merge() |
归并两个有序序列 |
inplace_merge() |
归并两个接续的有序序列 |
|
有序结构上的集合操作 |
includes() |
一序列为另一序列的子序列时为真 |
set_union() |
构造两个集合的有序并集 |
|
set_intersection() |
构造两个集合的有序交集 |
|
set_difference() |
构造两个集合的有序差集 |
|
set_symmetric_difference() |
构造两个集合的有序对称差集(并-交) |
|
堆操作 |
push_heap() |
向堆中加入元素 |
pop_heap() |
从堆中弹出元素 |
|
make_heap() |
从序列构造堆 |
|
sort_heap() |
给堆排序 |
|
最大和最小 |
min() |
两个值中较小的 |
max() |
两个值中较大的 |
|
min_element() |
序列中的最小元素 |
|
max_element() |
序列中的最大元素 |
|
词典比较 |
lexicographical_compare() |
两个序列按字典序的第一个在前 |
排列生成器 |
next_permutation() |
按字典序的下一个排列 |
prev_permutation() |
按字典序的前一个排列 |
make_heap(c.begin(),c.end()); //默认建立一个堆
push_heap(c.begin(),c.end()) //新添加一个元素在末尾,然后重新调整堆序。也就是把元素添加在底层vector的end()处。
pop_heap(c.begin(),c.end()); //把堆顶元素取出来,放到了数组或者是vector的末尾,用原来末尾元素去替代
sort_heap(c.begin(),c.end()); //持续对整个heap做pop_heap操作