STL中的几个基本概念
- 容器:可容纳各种数据类型的数据结构。(如
vector
) - 迭代器:可依次存取容器中元素的东西
- 算法:用来操作容器中的元素的函数模板。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象
比如,数组int array[100]就是一个容器,而int *类型的指针变量就可以作为迭代器,可以为这个容器编写一个排序的算法
顺序容器
vector
: 后部插入/删除,直接访问deque
: 前/后插入/删除,直接访问list
: 双向链表,任意位置插入/删除关联容器
set
: 快速查找,无重复元素
multiset
: 快速查找,可有重复元素map
: 一对一映射,无重复元素,基于关键字查找multimap
: 一对一映射,可有重复元素,基于关键字查找前两者合称为第一类容器
容器适配器
stack
: LIFOqueue
: FIFOpriority_queue
: 优先级高的元素先出
顺序容器简介
vector
头文件<vector>
实际上就是个动态数组。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能
deque
头文件<deque>
也是个动态数组,随机存取任何元素都能在常数时间内完成(但性能次于
vector
)。在两端增删元素具有较佳的性能。list
头文件<list>
双向链表,在任何位置增删元素都能在常数时间完成。不支持随机存取
上述三种容器称为顺序容器,是因为元素的插入位置同元素的值无关
关联容器简介
关联容器内的元素是排序的,插入任何元素,都按相应的排序准则来确定其位置。关联式容器的特点是在查找时具有非常好的性能。
set/multiset
头文件<set>
set
即集合。set
中不允许相同的元素,multiset
中允许存在相同的元素map/multimap
头文件<map>
map
与set
的不同在于map
中存放的是成对的key/value
并根据
key
对元素进行排序,可快速地根据key
来检索元素map
同multimap
的不同在于是否允许多个元素有相同的key
值上述4种容器通常以平衡二叉树方式实现,插入和检索的时间都是O(logN)
容器的共有成员函数
所有标准容器共有的成员函数
相当于按词典顺序比较两个容器大小的运算符:
=,<,<=,>,>=,==,!=
empty
: 判断容器中是否有元素
max_size
: 容器中最多能装多少个元素size
: 容器中元素的个数swap
: 交换两个容器的内容只在第一类容器中的函数
begin
: 返回指向容器中第一个元素的迭代器end
: 返回指向容器中最后一个元素后面的位置的迭代器rbegin
: 返回指向容器中最后一个元素的迭代器rend
: 返回指向容器中第一个元素前面的位置的迭代器erase
: 从容器中删除一个或几个元素
迭代器
定义:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
用于指向第一类容器中的元素。有const和非const两种。通过迭代器可以读取它指向的元素,通过非const迭代器还能修改其指向的元素。迭代器用法和指针类似
定义一个容器类的迭代器的方法可以是:
容器类名::iterator 变量名
或 容器类名::const_iterator 变量名
访问一个迭代器指向的元素:
*迭代器变量名
迭代器上可以执行++
操作,以指向容器中下一个元素。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end
值
使用一个past-the-end
值的迭代器来访问对象是非法的,就好像使用NULL
或未初始化的指针一样。
不同容器上支持的迭代器功能强弱有所不同
容器的迭代器的功能强弱,决定了该容器是否支持STL
中的某种算法
- 只有第一类容器能用迭代器遍历
- 排序算法需要通过随机迭代器来访问容器中的元素,那么有的容器就不支持排序算法。
STL中的迭代器
STL
中的迭代器按功能有弱到强分为5种
- 输入:
Input iterators
提供对数据的只读访问 - 输出:
Output iterators
提供对数据的只写访问 - 正向:
Forward iterators
提供读写操作,并能一次一个地向前推进迭代器 - 双向:
Bidirectional iterators
提供读写操作,并能一次一个地向前和向后移动。 - 随机访问:
Random access iterators
提供读写操作,并能在数据中随机移动
编号大的迭代器拥有编号小的迭代器的所有功能,能当做编号小的迭代器使用
不同迭代器所能进行的操作(功能)
所有迭代器:
++p
,p++
输入迭代器:
*p
,p = p1
,p == p1
,p != p1
输出迭代器:
*p
,p = p1
正向迭代器:上面全面
双向迭代器:上面全部,
--p
,p--
随机访问迭代器:上面全部,以及
p+=i
,p-=i
p+i
: 返回指向p后面的第i
个元素的迭代器p-i
: 返回指向p下面的第i
个元素的迭代器p[i]
: p后面的第i个元素的引用p<p1
,p<=p1
,p>p1
,p>=p1
容器所支持的迭代器类别
容器 | 迭代器类别 |
---|---|
vector | 随机 |
deque | 随机 |
list | 双向 |
set/multiset | 双向 |
map/multimap | 双向 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
算法简介
STL中提供能在各种容器中通用的算法,比如插入,删除,查找,排序等。大约有70种标准算法
算法举例
template<class InIt,class T>
InIt find<InIt first,InIt last,const T& val>;
first和last这两个参数都是容器的迭代器,它们给出了容器中的查找区间起点和终点,这个区间是左闭右开的区间。函数的返回值是一个迭代器,如果找到,该迭代器指向被找到的元素,如果没有找到,该迭代器指向查找区间的终点。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int array[10] = {10,20,30,40};
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::iterator p;
p = find(v.begin(),v.end(),3);
if(p != v.end())
cout<<*p<<endl;
p = find(v.begin(),v.end(),9);
if(p == v.end())
cout<<"not found"<<endl;
p = find(v.begin()+1,v.end()-2,1);
if(p != v.end())
cout<<*p<<endl;
int *pp = find(array,array+4,20);
cout<<*pp<<endl;
return 0;
}
顺序容器
除了上述共同操作外,顺序容器还有以下共同操作
front()
: 返回容器中第一个元素的引用
back()
: 返回容器中最后一个元素的引用
push_back()
: 在容器末尾增加新元素
pop_back()
: 删除容器末尾的元素
对于list<double>
list<double>::reference
实际上就是double &
list<double>::const_reference
实际上就是const double &
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int i;
int a[5] = {1,2,3,4,5};
vector<int> v(5);
cout<<v.end()-v.begin()<<endl;
for(i = 0 ; i < v.size() ; i++)
v[i] = i;
v.at(4) = 100;
for(i = 0 ; i < v.size() ; i++)
cout<<v[i]<<",";
cout<<endl;
vector<int> v2(a,a+5); // 构造函数
v2.insert(v2.begin()+2,13); // 在begin()+2位置插入法13
for(i = 0 ; i < v2.size() ; i++)
cout<<v2[i]<<",";
cout<<endl;
return 0;
}
#include<iostream>
#include<vector>
using namespace std;
int main()
{
const int SIZE = 5;
int a[SIZE] = {1,2,3,4,5};
vector<int> v(a,a+5); // 构造函数
try{
v.at(100) = 7;
}
catch(out_of_range e){
cout<<e.what()<<endl;
}
cout<<v.front()<<","<<v.back()<<endl;
v.erase(v.begin());
ostream_iterator<int> output(cout,"*");
copy(v.begin(),v.end(),output);
v.erase(v.begin(),v.end()); // 等效于v.clear()
if(v.empty())
cout<<"empty"<<endl;
v.insert(v.begin(),a,a+SIZE);
copy(v.begin(),v.end(),output);
return 0;
}
list容器
除了具有所有顺序容器都有的成员函数以外,还支持8个成员函数
push_front
: 在前面插入pop_front
: 删除前面的元素sort
: 排序(list不支持STL的算法sort)remove
: 删除和指定值相等的所有元素unique
: 删除所有和前一个元素相同的元素merge
: 合并两个链表,并清空被合并的那个reverse
: 颠倒链表splice
: 在指定位置前面插入另一链表中的一个或多个元素,并在另一个链表中删除被插入的元素
deque容器
所有适用于vector的操作都适用于deque
deque
还有push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作
关联容器
除了各容器都有的函数外,还支持一下成员函数:设m表示容器,k表示键值
m.find(k)
: 如果容器中存在建为k的元素,则返回指向该元素的迭代器。如果不存在,则返回end()
值。m.lower_bound(k)
: 返回一个迭代器,指向键不小于k的第一个元素m.upper_bound(k)
: 返回一个迭代器,指向键大于k的第一个元素m.count(k)
: 返回m中k的出现次数- 插入元素用
insert
pair模板
pair模板类用来绑定两个对象为一个新的对象,该类型在头文件中定义
pair<T1,T2> p1
: 创建一个空的pair
对象,它的两个元素分别是T1和T2类型,采用值初始化pair<T1,T2> p1(v1,v2)
make_pair(v1,v2)
: 以v1
和v2
值创建一个新的pair
对象,其元素类型分别是v1
和v2
类型p.first
: 返回p
中名为first
的(公有)数据成员p.second
: 返回p
中名为second
的(公有)数据成员
#include<iostream>
#include<set>
using namespace std;
int main(void)
{
typedef set<double,less<double> >double_set;
const int SIZE = 5;
double a[SIZE] = {2.1,4.2,9.5,2.1,3.7};
double_set doubleSet(a,a+SIZE);
pair<double_set::const_iterator,bool> p;
p = doubleSet.insert(9.5);
if(p.second)
cout<<"1)"<<*(p.first)<<" inserted"<<endl;
else
cout<<"2)"<<*(p.first)<<" not inserted"<<endl;
return 0;
}
输出:
2) 9.5 not inserted
缺省情况下用less定义关键字的“小于”关系
map
#include<iostream>
#include<map>
using namespace std;
ostream &operator <<(ostream &o,const pair<int,double>& p)
{
o<<"("<<p.first<<","<<p.second<<")";
return o;
}
int main()
{
typedef map<int,double,less<int> > mmid;
mmid pairs;
cout<<"1)"<<pairs.count(15)<<endl;
//pairs.insert(mmid::value_type(15,2.7));
pairs.insert(make_pair(15,99.3)); // make_pair生成pair对象
cout<<"2)"<<pairs.count(15)<<endl;
pairs.insert(mmid::value_type(20,9.3));
mmid::iterator i;
cout<<"3)";
for(i = pairs.begin() ; i != pairs.end() ; i++)
cout<<*i<<",";
cout<<endl;
cout<<"4)";
int n = pairs[40]; // 如果没有关键字为40的元素,则插入一个
for(i = pairs.begin() ; i != pairs.end() ; i++)
cout<<*i<<",";
cout<<endl;
cout<<"5)";
pairs[15] = 6.28; // 把关键字为15的元素值改成6.28
for(i = pairs.begin() ; i != pairs.end() ; i++)
cout<<*i<<",";
return 0;
}
运行结果
容器适配器: stack
stack是后进先出的数据结构,只能插入,删除,访问栈顶的元素
stack上可以进行以下操作
push
: 插入元素pop
:弹出元素top
:返回栈顶元素的引用
容器适配器: queue
同样也有push
,pop
,top
函数
但是push
发生在队尾,pop
,top
发生在对头,先进先出
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
const int SIZE = 10;
int a1[] = {2,8,1,50,3,100,8,9,10,2};
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");
vector<int>::iterator location;
location = find(v.begin(),v.end(),10);
if(location != v.end()){
cout<<endl<<"1)"<<location-v.begin();
}
sort(v.begin(),v.end());
if(binary_search(v.begin(),v.end(),9))
cout<<endl<<"3)"<<"9 found";
else
cout<<endl<<"3)"<<"9 not found";
}