一、概述
1.1 STL的概念和作用
-
STL的概念:全称为 Standard Template Library
-
STL的作用:
首先STL并不是语言的一部分(一开始并没有)它就是一个工具库, 没有这个工具时程序员写程序都要自己做(例如:数据结构中的 链表,堆栈…)
STL模板库内部使用模板使操作更加泛化, STL内部三大部分构成(容器,泛型以及算法)
1.2 数组结构和链表结构
-
数组结构
优点:随机访问方便,速度快效率高。
缺点:插入删除不方便,效率低(内存空间分布的限制)
只要数据存储在连续内存空间中即为数组结构。 -
链表结构
优点:插入删除操作方便,效率高。
缺点:随机访问不方便效率低,往往就是通过在遍历过程中对给定的条件进行检测。
只要数据存储在非连续内存空间中,但数据之间互相关联即为链式结构。 -
总结:STL模板库中所提供的容器类,结合了数组和链表的优缺点,使用户从诸如内存管理的细节中得以解脱(对数组和链表的操作进行了封装)
1.3 十大容器概述
-
向量(vector):类似数组(内部是线性存储)支持下标访问,在尾部添加和删除元素效率高,中间执行添加删除操作可以,但效率很低。
-
双端队列( deque ) : 支持下标访问(头尾两端都可添加/删除操作)。
-
列表(list):在任何位置添加和删除操作都很方便,不支持下标访问。
-
堆栈(stack):支持在一端存储和提取元素。
-
队列(queue):支持从前端提取,后端压入元素。
-
优先队列(priority_queue):类似队列,但所提取的是具有最高优先级的元素(默认大者优先)。
-
映射(map):以 key-value对 的形式存储数据,以key的升序排列,key唯一(内部结构是红黑树)。
-
多重映射(multimap):允许key重复出现的映射。
-
集合(set):没有value的映射 。
-
多重集合(multiset):没有value的多重映射。
向量、双端队列和链表称为基本容器。
容器分类
-
线性(基本)容器:(向量,双端队列,列表)这类容器元素按照线性顺序(可以通过一个元素找到它的下一个元素即为线性)排列,必须支持某种形式的next操作,以便从一个元素移动到下一个元素(迭代)。
-
适配器容器 :(堆栈,队列,优先队列)这类容器是对线性容器的一些接口加以屏蔽的产物。
-
关联容器 :(映射,多重映射,集合,多重集合)这类容器根据一个元素相关联的key来存储或提取数据元素,存储是以key-value对的形式,按照key的升序(二叉树存储)。
十大容器的共同特点
- 所有容器都支持拷贝构造 和 拷贝赋值。
- 相同类型的两个容器之间可以通过==进行相等性判断。
- 容器存储的为数据的副本这也就意味着存入容器中的对象应支持拷贝构造和拷贝赋值。
- 通常情况下被存放到容器中的对象应支持无参构造。
1.4 迭代器
-
顺序迭代器
一次只能向后或者向前迭代一步,只支持++和–运算。 -
随机迭代器
即能一次向后或者向前迭代一步,也可以迭代多步,除了支持++和–,也支持对整数的加减运算。
除了向量和双端队列以及优先队列支持随机迭代器以外,其余容器只支持顺序迭代器。
-
正向迭代器:起始迭代器指向 向量第一个元素位置,终止迭代器指向向量最后一个元素的下一个位置,增操作向容器的尾部移动,减操作 向容器的首部移动。
-
反向迭代器:起始迭代器指向向量的最后一个元素的位置,终止迭代器指向向量第一个元素的前一个位置,增操作向容器的首部移动,减操作向容器的尾部移动。
四个迭代器类:
iterator / const_iterator / reverse_iterator / const_reverse_iterator
八个迭代器对象:
- begin() / end()
- begin()const / end()
- const rbegin() / rend()
- rbegin()const / rend()const
二、向量容器
2.1 成员函数
front() / back() / insert() / erase() ——获取头结点数据/尾结点数据;指向位置添加/删除节点
push_back() / pop_back() / empty() / clear()——添加/删除尾结点;判断空/清空向量
size() –向量维护元素个数 ,向量已经存储的数据个数
resize() - 设置向量元素个数 ,
capacity() - 获取向量容量 ,向量最多可以存储的数据个数
reserve() -设置向量的容量
容量反映向量维护多少内存;大小反映已经占了多少内存
2.2 初始化
向量中的元素被存储在一段连续的内存空间中。
向量维护的内存空间会随着新元素的增加而自动增长。
内存空间的连续性不会妨碍向量元素的增加,如果内存空间无法满足新元素的增加,向量会开辟新的足够的连续内存空间,并把原内存空间的数据复制到新的内存空间,释放原内存空间。
向量的增加会伴随着内存空间的分配和释放,元素复制和销毁等额外开销。
如果能够在创建向量时,合理预分配一些空间将很大程度上缓解这些额外开销。(reserve())
vector01.cpp
#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
class Student{
public:
Student(string const& name=""):m_name(name){
cout << "缺省构造了:" << m_name << "(" << this << ")" << endl;
}
Student(Student const& that):m_name(that.m_name){
cout << "用:" << that.m_name << "(" << &that << ")"
<< "拷贝构造了:" << m_name << "(" << this << ")" << endl;
}
~Student(){
cout << "析构了:" << m_name << "(" << this << ")" << endl;
}
private:
string m_name;
};
int main(){
vector<Student> vs;
vs.reserve(10); //p4
vs.push_back(Student("张三"));//p1
vs.push_back(Student("李四"));//p2
vs.push_back(Student("王五"));//p3
getchar();
return 0;
}
p1
p2
副本造副本说明向量内存空间不够,直接向量内存空间搬家。
p3
p4
2.3 迭代器的使用
增操作: insert
删操作 : erase
改操作
查操作 : find
排序操作 : sort
三、向量迭代器
增操作: insert
删操作 : erase
改操作
查操作 : find
排序操作 : sort
vector02.cpp
#include <iostream>
#include <vector>
#include <algorithm> // find / sort
using namespace std;
class Student{
public:
Student(string const& name="",int age=0):m_name(name),m_age(age){}
bool operator==(Student const& that)const{
return m_name==that.m_name && m_age==that.m_age;
}
bool operator<(Student const& that)const{
return m_age > that.m_age;
}
private:
string m_name;
int m_age;
friend ostream& operator<<(ostream& os, Student const& that);
};
ostream& operator<<(ostream& os, Student const& that){
return os << that.m_name << ":" << that.m_age;
}
void print(string const& str, vector<Student>& v){
cout << str << endl;
typedef vector<Student>::iterator IT;
for(IT it=v.begin(); it!=v.end(); ++it)
cout << *it << ' ';
cout << endl << "----------------------" << endl;
}
class CMP{
public:
bool operator()(Student const& a, Student const& b){
return a < b;
}
};
int main(){
vector<Student> vs;
vs.reserve(10);
vs.push_back(Student("张飞",22));
vs.push_back(Student("赵云",20));
vs.push_back(Student("关羽",25));
vs.push_back(Student("马超",27));
vs.push_back(Student("黄忠",44));
print("添加节点后:", vs);
vs.insert(vs.begin(),Student("刘备",38));
print("在迭代器指向的位置添加节点后:", vs);
vs.erase(vs.begin());
print("删除迭代器指向的节点后:", vs);
typedef vector<Student>::iterator IT;
IT it=vs.begin();
*it = Student("诸葛亮",18);
print("更改迭代器指向的节点后:", vs);
IT fit = find(vs.begin(),vs.end(),Student("赵云",20));//"=="
if(fit!=vs.end())
vs.erase(fit);
print("找到赵云并删除后:", vs);
// sort(vs.begin(),vs.end());// "<"
CMP cmp;//比较器
sort(vs.begin(), vs.end(), cmp);//比较器
print("排序后:", vs);
return 0;
}
正向迭代:
typedef vector<Student>::iterator IT;
for(IT it=v.begin(); it!=v.end(); ++it)
cout << *it << ' ';
反向迭代
typedef vector<Student>::reverse_iterator rIT;
for(rIT it = v.rbegin(); it != v.rend(); ++it)
cout << *it << ' ';
四、双端队列/列表
4.1 双端队列和向量差别
-
和向量差别就是首尾两端同样都是开放的,因此他同时提供了首尾两端增删元素的接口。
-
没有提供设置/获取容量的函数,设置和获取容器大小的函数存在。
deque.cpp
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
class Student{
public:
Student(string const& name="",int age=0):m_name(name),
m_age(age){}
bool operator==(Student const& that)const{
return m_name==that.m_name && m_age==that.m_age;
}
bool operator<(Student const& that)const{
return m_age > that.m_age;
}
private:
string m_name;
int m_age;
friend ostream& operator<<(ostream& os, Student const& that);
};
ostream& operator<<(ostream& os, Student const& that){
return os << that.m_name << ":" << that.m_age;
}
void print(string const& str, deque<Student>& d){
cout << str << endl;
//正向迭代
typedef deque<Student>::iterator IT;
for(IT it=d.begin(); it!=d.end(); ++it){
cout << *it << ' ';
}
cout << endl << "----------------" << endl;
}
int main(){
deque<Student> di;
di.push_front(Student("张飞",22));
di.push_front(Student("赵云",20));
di.push_front(Student("马超",26));
di.push_back(Student("关羽", 28));
di.push_back(Student("黄忠", 44));
print("添加节点后:", di);
typedef deque<Student>::iterator IT;
di.insert(di.begin(),Student("刘备",50));
print("在迭代器指向的位置添加节点后:", di);
di.erase(di.begin());
print("删除迭代器指向的节点后:", di);
IT it=di.begin();
*it = Student("吕布",36);
print("更改迭代器指向的节点后:", di);
//"=="
IT fit = find(di.begin(),di.end(),Student("黄忠",44));
if(fit!=di.end())
di.erase(fit);
print("找到黄忠并删除后:", di);
sort(di.begin(),di.end());//"<"
print("排序后:", di);
return 0;
}
4.2 列表
- 唯一化
void unique(void); 将连续重复出现的元素唯一化 - 排序(都是全局排序)注意sort是成员函数
void sort(void); 通过 < 比大小
templatevoid sort(LESS less); 通过比较器比大小
向量排序需要交换元素位置,但链表排序无需交换位置,改变指针指向即可。
- 拆分:将参数列表中的部分或全部元素剪切到调用列表中
templatevoid splice( IT pos, list& lst );//全部
templatevoid splice( IT pos, list& lst, IT del);//一个
templatevoid splice( IT pos, list& lst, IT begin, IT end);//部分
list.cpp
#include <iostream>
#include <list>
using namespace std;
class CMP{
public:
bool operator()(int const& a, int const& b){
return a > b;
}
};
void print(string const& str, list<int>& l){
cout << str << endl;
typedef list<int>::iterator IT;
for(IT it=l.begin(); it!=l.end(); ++it)
cout << *it << ' ';
cout << endl << "---------------" << endl;
}
int main(){
list<int> ls;
for(int i=0; i<5; i++)
ls.push_front(10+i);
for(int i=0; i<5; i++)
ls.push_back(10-i);
print("添加节点后:", ls);
ls.unique();
print("唯一化后:", ls);
// ls.sort();
CMP cmp;//比较器
ls.sort(cmp);
print("排序后:", ls);
list<int> lst;
lst.push_back(1000);
lst.push_back(2000);
lst.push_back(3000);
lst.push_back(4000);
// ls.splice(ls.begin(),lst);
// ls.splice(ls.begin(),lst,lst.begin());
ls.splice(ls.begin(),lst,++lst.begin(),--lst.end());
print("ls:", ls);
print("lst:", lst);
return 0;
}
五、适配器容器
5.1 栈
-
定义形式
stack<元素类型,[底层容器类型]> 堆栈对象(构造实参表) -
底层容器:vector / deque(默认) / list / 自己实现的容器作为栈的底层容器
-
成员函数
push ->push_back
pop ->pop_back
top -> back
size -> size
empty -> empty
clear -> clear -
栈的底层实现
tempalate<class T,class D=deque<T>> class stack{
public:
void push(T data) { m_d.push_back(data); }
void pop() { m_d.pop_back(); }
……
private:
D m_d; //deque<int> m_d//vector<int> m_d
};
stack s; 或 stack<int,vector> s; 或 stack<int,list> s;
stack.cpp
nclude <iostream>
#include <stack>
#include <vector>
#include <deque>
#include <list>
using namespace std;
int main(){
stack<int,deque<int> > s;//若不写deque?vector,默认也是deque
s.push(1);
s.push(2);
s.push(3);
s.push(4);
s.push(5);
s.push(6);
while(!s.empty()){
cout << s.top() << endl;
s.pop();
}
return 0;
}
5.2 队列
-
定义形式
queue<元素类型,[底层容器类型]> 队列对象(构造实参表) -
底层容器:deque(默认) / list / vector不可以(向量不支持头部删除功能)
-
成员函数
push ->push_back
pop ->pop_front(向量没有这个方法 )
back - > back
front -> front
size -> size / empty -> empty / clear -> clear -
队列的底层实现
tempalate<class T,class D=deque> class queue{
public:
void push(T data) { m_d.push_back(data); }
void pop() { m_d.pop_front(); }
……
private:
D m_d; //
};
queue s; 或 queue<int,vector> s(这个方法是错误的); 或 queue<int,list> s;
queue.cpp
#include <iostream>
#include <queue>
#include <vector>
#include <deque>
#include <list>
using namespace std;
int main(){
queue<int,deque<int> > s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
s.push(5);
s.push(6);
while(!s.empty()){
cout << s.front() << endl;
s.pop();
}
return 0;
}
5.3 优先队列
- 定义形式:
priority_queue<元素类型,【底层容器类型】,【比较器类型】>
优先队列对象(构造实参表) - 底层容器:deque(默认) /vector
(支持随机迭代,不能list) - 注意事项:
优者先出,默认以大者为优也可以通过比较器定制(比- 较器必须是类),
如果没有“比较器”默认内部使用<运算符。
并不是出队时挑,而且进队列时就保证有序 - 成员函数
push /pop / top / empty / size / clear
priority.cpp
#include <iostream>
#include <queue>
#include <vector>
#include <deque>
#include <list>
using namespace std;
class CMP{
public:
bool operator()(int const& a, int const& b){
return a > b;
}
};
int main(){
priority_queue<int,deque<int>,CMP> pq;
pq.push(3);
pq.push(8);
pq.push(4);
pq.push(9);
pq.push(5);
pq.push(7);
while(!pq.empty()){
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
六、关联容器
6.1 映射
头文件:map
-
定义形式: map<键类型,值类型> 映射对象;
-
逻辑模型:–对应模型 键(信息索引)值(信息内容)对,主要用于信息检索,性能可以达到对数级(O(logN)),类似二分法。
-
物理模型:平衡有序二叉树又名红黑树。
-
红黑树
此时5以下的节点超过2层,以4为中心,顺时针转,即变为:
此时又不符合平衡,以3为中心顺时针旋转:
树形结构的最大好处:检索速度快。
-
键必须唯一。
-
迭代过程实际上是关于键的中序遍历(L D R),键的升序。
-
存储单位是由键和值组成的pair。
template<class FIRST,class SECOND>class pair{
public:
pair(FIRST const& f, SECOND const& s) : first(f),second(s){}
FIRST first; //键
SECOND second;//值
}
映射的迭代器相当于指向pair对象的指针。
- 映射中的键是只读的。
- 检索性能好(一砍砍一半)构建和修改性能较差 ,适用于结构稳定,但是需要频繁检索的操作。
- 支持“下标”运算,用键作为下标,得到对应的值的引用,如果所给出的键不存在,增加一个节点,返回其值的引用。
- 成员函数
insert( pair<FIRST,SECOND>(键,值) )
insert( make_pair(键,值))
迭代器 = find(键);
失败返回终止迭代(并非全局函数而是 成员函数)
#include <iostream>
#include <map>
using namespace std;
class Candidate{
public:
Candidate(string name=""):m_name(name),m_vote(0){}
string m_name;
int m_vote;
};
void print(map<char,Candidate>& m){
typedef map<char,Candidate>::iterator IT;
for(IT it=m.begin(); it!=m.end(); ++it){
cout << "(" << (*it).first << ")" <<
(*it).second.m_name << ' ';
}
cout << endl;
}
int main(){
map<char,Candidate> m;
m.insert(pair<char,Candidate>('A',Candidate("张飞")));
m.insert(make_pair('B',Candidate("关羽")));
//映射容器支持下标操作
m['C'] = Candidate("赵云");
m['D'] = Candidate("马超");
m['E'] = Candidate("黄忠");
typedef map<char,Candidate>::iterator IT;
for(int i=0; i<10; i++){
print(m);
char ch;
cin >> ch;
IT fit = m.find(ch);
if(fit==m.end()){
cout << "废票" << endl;
continue;
}
++(*fit).second.m_vote;
}
for(IT it=m.begin(); it!=m.end(); ++it){
cout << (*it).second.m_name << ":"
<< (*it).second.m_vote << endl;
}
return 0;
}
6.2 多重映射
允许键重复的映射,表示一对多的逻辑关系,不支持下标运算符。
比如一个学生,既有期中成绩又有期末成绩。
multiMap.cpp
#include <iostream>
#include <map>
using namespace std;
int main(){
multimap<string,int> m;
m.insert(make_pair("张飞",80));
m.insert(make_pair("赵云",70));
m.insert(make_pair("关羽",60));
m.insert(make_pair("张飞",50));
m.insert(make_pair("赵云",40));
m.insert(make_pair("关羽",30));
typedef multimap<string,int>::iterator IT;
for(IT it=m.begin(); it!=m.end(); ++it){
cout << (*it).first << ":" << (*it).second << ' ';
}
cout << endl;
return 0;
}
6.3 集合
-
没有值只有键的映射
-
与向量等基本容器相比最大优势就是 排重(排除重复元素)。
内部结构为红黑树:检索信息快。
set.cpp
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(3);
cout << "节点个数:" << s.size() << endl;
typedef set<int>::iterator IT;
for(IT it=s.begin(); it!=s.end(); ++it)
cout << (*it) << ' ';
cout << endl;
return 0;
}
6.4 多重集合
- 没有值只有键的多重映射。
其内部结构仍然为红黑树:检索效率高于普通容器。
multiSet.cpp
#include <iostream>
#include <set>
using namespace std;
int main(){
multiset<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(3);
cout << "节点个数:" << s.size() << endl;
typedef multiset<int>::iterator IT;
for(IT it=s.begin(); it!=s.end(); ++it)
cout << (*it) << ' ';
cout << endl;
return 0;
}
七、总结
主要四大容器:向量、双端队列、列表、映射。