以下内容来源书上网上 经过整合而成
一、STL一般介绍
STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同;算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方;迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数对象是类似于函数的对象,可以是类类对象或函数指针(包括函数名,因为函数名被用途指针)。STL使得能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排列)。
二、STL的六大组件
- 容器(Container),是一种数据结构,如list,vector,和deques,以模板类的方法提供 。为了访问容器中的数据,可以使用由容器类输出的迭代器。
- 迭代器(Iterator),提供了访问容器中对象的方法。
- 算法(Algorithm),是用来操作容器中的数据的模板函数。
- 仿函数(Function object,仿函数又称为函数对象),其实就是重载了()操作符的struct.
- 迭代适配器
- 空间配制器
三、STL容器
1、序列式容器(Sequence containers)
- deque(表示的双端队列允许在两端添加和删除元素,与vector类似)、list(表示非连续的内存,基于链表实现)、queue、priority_queue、stack和vector(表示一段连续的内存,基于数组实现)
- 序列要求其元素按严格的线性顺序排列,即存在第一个元素,最后一个元素,除第一个元素和最后一个元素外,每个元素前后都分别有一个元素。数组和链表都是序列,但分支结构(其中每个节点都指向两个子节点)不是序列。
- 因为序列中的元素具有确定的顺序,因此可以执行诸如将值插入到特定位置、删除特定区间等操作。
(1)vector【基于模板实现,需包含#include <vector> using namespace std;】
vector是数组的一种类表示,它提供了自动内存管理功能,可以动态地改变vector对象的长度,并随着元素的添加和删除而增大和缩小,它提供了对元素的随机访问,在尾部添加和删除元素的时间固定的,但在头部或中间插入和删除元素的复杂度为线性时间。
除了序列外,vector可反转容器,增加两个类方法:
rbegin():返回一个指向反转序列的第一个元素的迭代器
rend():返回反转序列的超尾迭代器
vector常用操作:
//1、定义和初始化
vector<int> vec1;//默认初始化,vec1为空
vector<int> vec2(vec1); //使用vec1初始化vec2
vector<int> vec3(vec1.begin(), vec1.end()); //使用vec1初始化vec2
vector<int> vec4(10); //10个值为0的元素
vector<int> vec5(10, 4); //10个值为4的元素
//2、常用操作方法
vec1.push_back(100); //添加元素
int size = vec1.size(); //元素个数
bool isEmpty = vec1.empty(); //判断是否为空
cout << vec1[0] << endl; //取得第一个元素
vec1.insert(vec1.end(), 5, 3); //从vec1.end()位置插入5个值为3的元素
//vec1.pop_back(); //删除末尾元素
//vec1.erase(vec1.begin(), vec1.end()); //删除之间的元素,其他元素前移
vector<int>::iterator iter = vec1.begin(); //获取迭代器首地址
vector<int>::const_iterator c_iter = vec1.begin(); //获取const类型迭代器
//vec1.clear(); //清空元素
//3、遍历
//下标法
int length = vec1.size();
for(int i = 0; i < length; i++)
{
cout << i << ": " << vec1[i] << endl;
}
//迭代器法
vector<int>::const_iterator iterator = vec1.begin();
for(; iterator != vec1.end(); iterator++)
{
cout << *iterator << endl;
}
//迭代器反向显示
vector<int>::reverse_iterator iterator1 = vec1.rbegin();
for(; iterator1 != vec1.rend(); iterator1++)
{
cout << *iterator1 << endl;
}
(2)list(双向链表,与向量vector相比,它允许快速的插入和删除,但是随机访问比较慢。需要添加头文件#include <list> using namespace std;)
list模板类(在list头文件中声明)表示双向链表。除了第一个和最后一个元素外,每个元素都与前后的元素相链接,这竟未着可以双向遍历链表。
list和vector关键区别:list在链表中任一位置进行插入和删除的时间都是固定的。vector强调的是通过随机访问进行快速访问,而list强调的是元素的快速插入和删除。
与vector相似,list也可反转容器。与vector不同的是,list不支持数组表示法和随机访问。与矢量迭代器不同,从容器中插入或删除元素之后,链表迭代器指向元素将不变。
//1、定义和初始化
list<int> lst1; //创建空list
list<int> lst2(3); //创建含有三个元素值为0的list
list<int> lst3(3, 2); //创建含有三个元素值为2的list
list<int> lst4(lst2); //使用lst2初始化lst4
list<int> lst5(lst2.begin(), lst2.end()); //同lst4
//2、常用操作方法
lst1.assign(lst2.begin(), lst2.end()); //分配值
lst1.push_back(10); //添加值
lst1.pop_back(); //删除末尾值
lst1.begin(); //返回产值的迭代器
lst1.end(); //返回尾值的迭代器
lst1.clear(); //清空值
bool isEmpty = lst1.empty(); //判断为空
lst1.erase(lst1.begin(), lst1.end()); //删除元素
lst1.front(); //返回第一个元素的引用
lst1.back(); //返回最后一个元素的引用
lst1.insert(lst1.begin(), 3, 2); //从指定位置插入3个值为2的元素
lst1.rbegin(); //返回第一个元素的前向指针
lst1.remove(2); //相同的元素2全部删除
lst1.reverse(); //反转
lst1.size(); //含有元素个数
lst1.sort(); //排序
lst1.unique(); //删除相邻重复个数
//3、遍历
for(list<int>::const_iterator iter = lst1.begin(); iter != lst1.end(); iter++)
{
cout << *iter << endl;
}
(3)deque
deque模板类表示双端队列(double-ended queue),通常被简称为deque。在STL中,实现类似于vector容器,支持随机访问。主要区别在于,从deque对象的开始位置插入和删除元素的时间是固定的,而不像vector中那样是线性时间的。deque还支持从开始端插入数据:push_front()。其余类似vector操作方法的使用。
(4)queue(在头文件queue中声明)
queue模板类是一个适配器类。queue模板的限制比deque更多,它不仅不允许随机访问队列元素,甚至不允许遍历队列。它把使用限制在定义队列的基本操作上。可以将元素添加到队尾、从队首删除元素、查看队首和队尾的值、检查元素数目和测试队列是否为空。
bool empty() const //判断队列是否为空,若为空,返回true,否则返回false
size_type size() const //返回队列中元素的数目
T & front() //返回指向队首元素的引用
T & back() //返回指向队尾元素的引用
void push(const T &x) //在队尾插入x
void pop() //删除队首元素
(5)priority_queue(在头文件queue中声明)
priority_queue模板类是另一个适配器类,它支持的操作与queue相同。两者之间的主要区别在于,在priority_queue中,最大的元素被移到队尾。内部区别在于,默认的底层类是vector,可以修改用于确定哪个元素放到队尾的比较方式,方法是提供一个可选的构造函数参数。
(6)stack(在头文件stack中声明)
stack是一个适配器类,它给底层类提供了典型的堆栈接口。它不仅不允许随机访问堆栈元素,甚至不允许遍历堆栈。它把使用限制在定义堆栈的基本操作上,即可以将奔往推到栈顶、从栈顶弹出元素、查看 栈顶的值、检查元素数目和测试堆栈是否为空。
bool empty() const //如果堆栈为空,则返回true;否则返回false
size_type size() const //返回堆栈中的元素数目
T & top() //返回指向栈顶元素的引用
void push(const T&x) //在堆栈顶部插入x
void pop() //删除栈顶元素
2、联合容器
- 联合容器将值与关键字关联在一起,使用关键字来查找值。例如:值可以是表示雇员信息的结构。对于容器X,表达式X::value_type通常指出了存储在容器中值的类型,对于联合容器来说,表达式X::key_type指出了用作关键字的类型。
- 联合容器的长处在于,它提供了对元素的快速访问。与序列相似,联合容器也允许插入新元素,不过不能指定元素的插入位置。原因是联合容器通常包含用于确定数据放置位置的算法,以便能够很快检索信息。
- STL提供了4种联合容器:(set、multiset【在set头文件中】) (map、multimap【在map头文件中】)
(1)set :值的类型与关键字相同,关键字是惟一的------集合中不会有多个相同的关键字。multiset类似于set,只是multiset可能在多个值的关键字相同。
set范例:STL set是多个概念的模型,它是一个联合集合,可反转,可排序,关键字是惟一的,所以它只能存储同一种类型的值。
1》set<string> A;
2》set<string, less<string> > A; //第二个模板参数用来指示对关键字进行排序的比较函数或对象。在默认情况下,用less<>模板
const int N = 6;
string s1[N] = {"buffoon", "thinkers", "for", "heavy", "can", "for"};
string s2[N] = {"metal", "any", "food", "elegant", "deliver", "for"};
set<string> A(s1, s1+N); //将迭代器的区间作为参数的构造函数
set<string> B(s2, s2+N);
ostream_iterator<string, char> out(cout, " ");
cout << "Set A:";
copy(A.begin(), A.end(), out);
cout << endl;
cout << "Set B:";
copy(B.begin(), B.end(), out);
cout << endl;
cout << "Union of A and B:\n";
set_union(A.begin(), A.end(), B.begin(), B.end(), out);
cout << endl;
cout << "Interdection of A and B:\n";
set_intersection(A.begin(), A.end(), B.begin(), B.end(), out);
cout << endl;
cout << "Defference of A and B:\n";
set_difference(A.begin(), A.end(), B.begin(), B.end(), out);
cout << endl;
set<string> C;
cout << "Set C:\n";
set_union(A.begin(), A.end(), B.begin(), B.end(),
insert_iterator<set<string> > (C, C.begin()));
copy(C.begin(), C.end(), out);
cout << endl;
string s3("qrungy");
C.insert(s3);
cout << "Set C after insertion:\n";
copy(C.begin(), C.end(), out);
cout << endl;
cout << "Showing a range:\n";
copy(C.lower_bound("ghost"), C.upper_bound("spook"), out);
cout << endl;
(2)map:值的类型与关键字不同,关键字是惟一的,每个关键字只对应一个值,multimap类型与map相似,只是一个关键字可以与多个值关联。
四、迭代器
1、STL遵循:
- 每个容器类定义了相应的迭代器类型;
- 每个容器类都有一个超尾标记,当迭代器递增到超越窗口的最后一个值后,这个值将被赋给迭代器。
- 每个容器类都有begin()和end()方法,分别返回一个指向容器的第一个元素和超尾位置的迭代器。
- 每个容器类都使用++操作,让迭代器从指向第一个元素逐步指向超尾位置,从而遍历容器中的每一个元素。
2、迭代器类型
- 输入迭代器:用来读取容器中的信息,需要输入迭代器的算法将不会修改容器中的值。
注意:输入迭代器是单向迭代器,可以递增,但不能倒退
- 输出迭代器:用于将信息从程序传输给容器的迭代器。解除引用让程序能修改容器值,而不能读取。
- 正向迭代器:只使用++操作符来遍历容器。正向迭代器既可以使得能够读取和修改数据,也可以使得只能读取数据
- 双向迭代器:具有正向迭代器的所有特性,同时支持两种(前缀和后缀)递减操作符
- 随机访问迭代器:具有双向迭代器的所有特性,同时添加了支持随机访问的操作
3、
(1)将指针用作迭代器
STL
- sort()函数接受指向容器第一个元素的迭代器和指向超尾的迭代器作为参数,对数组进行排序
- copy()将数据从一个容器复制到另一个容器中。
例:int casts[10] = {6, 7, 1, 3, 5, 6, 7, 9, 5, 2};
vector<int> dice[10];
copy(casts, casts + 10, dice.begin()); //前两个迭代器参数表示要复制的范围,最后一个迭代器表示要将第一个元素复制到什么位置
若要将信息复制到显示器上,如果有一个表示输出流的迭代器,则可以使用copy().
#include <iterator>
ostream_iterator<int, char> out_iter(cout, " ");
注意:rbegin()和end()返回相同的值(超尾),但类型不同(reverse_iterator和iterator)。同样,rend()和begin()也返回相同的值(指向同一个元素的迭代器),但类型不同。
五、函数对象
函数对象也叫函数符,函数符是可以以函数方式与()结合使用的任意对象。这包括函数名、指向函数的指针和重载了()操作符的类对象。
1、函数符概念
- 生成器(generator):不用参数就可以调用的函数符
- 一元函数(unary function):用一个参数可以调用的函数符
- 二元函数(binary function):用两个参数可以调用的函数符
六、算法
1、算法组(前三组在algorithm头文件中描述,第4组是专用于数值数据的,有自己的头文件,称为numeric)
- 非修改式序列操作:对区间中的每个元素进行操作,这些操作不修改容器的内容,例如:find()和for_each()就属于这一类
- 修改式序列操作:对区间中的每个元素进行操作,不过,可以修改容器的内容、值和排列顺序。例如,transform()、random_shuffle()和copy()
- 排序和相关操作 :排序和相关操作包括多个排序函数和其他各种函数,包括集合操作
- 通用数字运算 :包括将区间的内容累积、计算两个容器的内部乘积、计算小计、计算相邻对象差