回顾:
泛化编程 day01
泛化编程 day02
六、STL(标准模板库)
1、STL容器介绍
-
1)STL的概念全称为Standard Template Library(标准模板库)
-
2)STL的作用
- 首先STL并不是语言的一部分(一开始并没有)它就是一个工具库,没有这个工具时程序员写程序都要自己做容器(例如:数据结构中的链表,堆栈等)
- STL模板内部使用模板使操作更加泛化,STL内部两大部分构成:容器和泛型算法
-
3)数组和链表优缺点
- 数组:
- 优点:随机访问,速度快效率高
- 缺点:插入删除不方便,效率低(内存空间分布的限制)
- 链表:
- 优点:插入删除操作方便,效率高
- 缺点:随机访问不方便效率低,往往就是通向在遍历过程中对给定的条件进行检测。
- 总结:STL模板库中提供的容器类,结合了数组和链表的优缺点,使用户从诸如内存管理的细节中得以解脱(对数组和链表的操作进行了封装)
- 数组:
-
4)十大容器
- 向量(vector):类似数组(内部是线性存储)支持下标访问,在尾部添加和删除元素效率高,中间可以执行添加删除操作但效率低;
- 双端队列(deque):支持下标访问(头尾两端都支持添加/删除操作);
- 列表(list):在任何位置添加和删除操作都很方便,不支持下标访问;
- 堆栈(stack):支持在一端存储和提取元素;
- 队列(queue):支持从前端提取,后端压入元素;
- 优先队列(priority_queue):类似队列,但所提取的是具有最高优先级的元素(默认大者优先)
- 映射(map):以key-value对 的形式存储数据,以key的升序排列,key唯一(内部结构是红黑树);
- 多重映射(multimap):允许key重复出现的映射;
- 集合(set):没有val的映射;
- 多重集合(multiset):没有value的多重映射。
-
5)容器的分类
- 线性容器:(向量,双端队列,列表)这类容器元素按照线性顺序排列,必须支持某种形式的next操作,以便从一个元素移动到下一个元素(迭代)
- 适配器容器:(堆栈,队列,优先队列)这类容器是对线性容器的一些接口加以屏蔽的产物
- 关联容器:(映射,多重映射,集合,多重集合)这类容器根据一个元素相关联的key来存储或提取数据元素,存储是以key-value对的形式,按照key的升序(二叉树存储)
-
6)容器的共同特点
- 所有容器都支持拷贝构造和拷贝赋值
- 相同类型的两个容器之间可以通过
==
进行相等性判断 - 容器存储的为数据的副本这也意味着存入容器中的对象应支持拷贝构造和拷贝赋值
- 通常情况下被存放到容器中的对象应支持无参构造
七、十大容器
1、向量(vector)
-
1)成员函数
- front()/back()/insert()/erase()
- push_back()/pop_back()/empty()/clear()
- size()向量维护元素个数
- resize()设置向量元素个数
- capacity()获取向量容量
- reserve()设置向量的容量
-
2)向量的初始化
- 向量中的元素被存储在一段连续的内存空间中通过下标访问向量元素的效率与数组相当;
- 向量维护的内存空间回随着新元素的增加而自动增加;
- 内存空间多的连续性不会妨碍向量元素的增加,如果内存空间无法满足新元素的增加,向量会开辟新的足够的连续内存空间,并把原内存空间的数据复制到新的内存空间,释放原内存空间;
- 向量的增加会伴随着内存的分配和释放,元素复制和销毁等额外开销;
- 如果能够在创建向量时,合理分配一些空间将很大程度上缓解这些额外的开销。
eg:01vectorinit.cpp
#include <iostream>
#include <vector>
using namespace std;
void print(string const& str, vector<int>& v)
{
cout << str << endl;
cout << "向量的容量:" << v.capacity() << endl;
for(size_t i=0; i<v.capacity(); i++){
cout << v[i] << " ";
}
cout << endl;
cout << "向量的大小:" << v.size() << endl;
for(size_t i=0; i<v.size(); i++){
cout << v[i] << ' ';
}
cout << endl << "------------" <<endl;
}
int main(void)
{
vector<int> v1;//沒有維護任何的內存空間
print("v1:",v1);
vector<int> v2(10);//向量容量爲10,大小也爲10,存儲的數據都爲0
print("v2:", v2);
vector<int> v3(10, 5);//向量容量爲10,大小也爲10,存儲的數據都爲5
print("v3:", v3);
v3.push_back(100);//增加一個數據容量,容量增加不止一個
print("after v3 add a new data:", v3);
v3.pop_back();
print("after v3 delete a data:", v3);
v3.push_back(500);
print("v3 add data again:", v3);
return 0;
}
eg:02vector.cpp
#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
class Student
{
public:
Student(string const& name="", int const& age=0):
m_name(name), m_age(age){
cout << "缺省構造了:" << m_name << "(" << this << ")" << endl;
}
Student(Student const& that):m_name(that.m_name), m_age(that.m_age){
cout << "用" << that.m_name << "(" << &that << ")" << "拷貝構造了:"
<< m_name << "(" << this << ")" << endl;
}
~Student(){
cout << "析构了:" << m_name << "(" << this << ")" << endl;
}
private:
string m_name;
int m_age;
};
int main(void)
{
vector<Student> vs;
vs.reserve(10);
vs.push_back(Student("Adair"));
vs.push_back(Student("Rising"));
vs.push_back(Student("weeks"));
getchar();
return 0;
}
- 3)迭代器
- 顺序迭代器:一次只能向后或向前迭代一步,只支持
++
和--
运算; - 随即迭代器:既能一次向前或向后迭代一步,也可以迭代多步,除了支持
++
和--
,也支持对整数的加减运算,除了向量和双端队列以及优先队列随即迭代器以外,其余容器只支持顺序迭代器; - 正向迭代器:起始迭代器指向向量第一个元素位置,终止迭代器指向向量
最后一个元素的下一个位置
,增操作向容器的尾部移动,减操作向容器的首部移动; - 反向迭代器:起始迭代器指向向量最后一个元素位置,终止迭代器指向向量
第一个元素的前一个位置
,增操作向容器的首部移动,减操作向容器的尾部移动。 - 四个迭代器类:
- iterator/const_iterator
- reverse_iterator/const_reverse_iterator
- 八个迭代器对象:
- begin()/end()
- begin()const/end()const
- rebegin()/rend()
- rbegin()const/rend()const
- 顺序迭代器:一次只能向后或向前迭代一步,只支持
- 4)迭代器的使用
- 任何可能导致容器结构发生变化的函数被调用后,先前获取的迭代器可能失效,重新初始化迭代器再使用才是安全的。
eg:01vector.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T>
void print(string const& str, vector<T>& v){
cout << str << endl;
typedef typename vector<T>::iterator IT;
for(IT it=v.begin(); it!=v.end(); ++it)
cout << *it << ' ';
cout << endl << "---------" << endl;
}
class CMP
{
public:
bool operator()(int const& a, int const& b)const{
return a < b;
}
};
int main(void)
{
vector<int> vi;
for(int i=0; i<10; i++){
vi.push_back(10-i);
}
print("添加节点后:", vi);
vi.insert(vi.begin(), 300);
print("在迭代器指向位置添加节点后:", vi);
vi.erase(vi.begin());
print("删除迭代器指向的节点后:",vi);
typedef vector<int>::iterator IT;
IT fit = find(vi.begin(), vi.end(), 5);
if(fit!=vi.end())
vi.erase(fit);
print("找到元素5并删除后:", vi);
//sort(vi.begin(), vi.end());//内部用“<”s实现排序
CMP cmp;
sort(vi.begin(), vi.end(), cmp);//内部用比较器(cmp)排序
print("排序后:", vi);
return 0;
}
注意:
需要使用find()
函数需要添加头文件#include <algorithm>
eg:02vector.cpp
include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Student
{
public:
Student(string const& name="",int const& age=0):m_name(name),m_age(age){}
friend ostream& operator<<(ostream& os, Student const& s){
os << s.m_name << ","<< s.m_age;
return os;
}
bool operator==(Student const& that){
return m_name==that.m_name && m_age==that.m_age;
}
bool operator<(Student const& that){
return m_age < that.m_age;
}
private:
string m_name;
int m_age;
};
template<class T>
void print(string const& str, vector<T>& v){
cout << str << endl;
typedef typename vector<T>::iterator IT;
for(IT it=v.begin(); it!=v.end(); ++it)
cout << *it << ' ';
cout << endl << "---------" << endl;
}
emplate<class T>
class CMP
{
public:
bool operator()(T const& a, T const& b)const{
return a < b;
}
/*
public:
bool operator()(int const& a, int const& b)const{
return a < b;
}
bool operator()(Student const& a, Student const& b)const{
return a < b;
}
*/
};
int main(void)
{
/*
vector<int> vi;
for(int i=0; i<10; i++){
vi.push_back(10-i);
}
print("添加节点后:", vi);
vi.insert(vi.begin(), 300);
print("在迭代器指向位置添加节点后:", vi);
vi.erase(vi.begin());
print("删除迭代器指向的节点后:",vi);
typedef vector<int>::iterator IT;
IT fit = find(vi.begin(), vi.end(), 5);
if(fit!=vi.end())
vi.erase(fit);
print("找到元素5并删除后:", vi);
//sort(vi.begin(), vi.end());//内部用“<”s实现排序
CMP<int> cmp;
sort(vi.begin(), vi.end(), cmp);//内部用比较器(cmp)排序
print("排序后:", vi);
*/
vector<Student> vs;
vs.push_back(Student("weeks", 28));
vs.push_back(Student("adair", 25));
vs.push_back(Student("rising", 26));
vs.push_back(Student("zhou", 23));
vs.push_back(Student("ten", 28));
vs.push_back(Student("cent", 29));
print("添加节点后:", vs);
vs.insert(vs.begin(), Student("da", 25));
print("在迭代器制定的位置添加节点后:", vs);
vs.erase(vs.begin());
print("删除迭代器指向的节点后:", vs);
typedef vector<Student>::iterator IT;
IT fit = find(vs.begin(), vs.end(), Student("zhou", 23));
if(fit!=vs.end())
vs.erase(fit);
print("找到zhou并删除后:", vs);
//sort(vs.begin(), vs.end());
CMP<Student> cmp;
sort(vs.begin(), vs.end(), cmp);
print("排序后:",vs);
return 0;
}
- 5)查找
template<class IT, class value_type>
IT find(IT begin, IT end,value_type& key)//成功返回第一个匹配元素的迭代器,失败返回第二个参数
- 6)排序
//使用内部的sort函数
template<class IT>void sort(IT begin, IT end);
//自定义比较器比较
template<class IT, class LESS>
void sort(IT begin, IT end, LESS LSEE cmp);
2、双端队列(deque)
- 1)和向量差别就是首尾两端同样都是开放的,因此他同时提供了首尾两端增删元素的接口
- 2)没有提供设置/获取容量的函数,设置和获取容器大小的函数存在
eg:03deque.cpp
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
template<class T>
void print(string const& str, deque<T>& d){
cout << str << endl;
typedef typename deque<T>::iterator IT;
for(IT it=d.begin(); it!=d.end(); ++it){
cout << *it << ' ';
}
cout << endl << "-----------" << endl;
}
template<class T>
class CMP
{
public:
bool operator()(T const& a, T const& b){
return a > b;
}
};
int main(void)
{
deque<int> di;
for(int i=0; i<5; i++){
di.push_front(10-i);
}
for(int i=0; i<5; i++){
di.push_back(11+i);
}
print("添加节点后:", di);
di.pop_front();
di.pop_back();
print("删除头尾节点后:", di);
di.insert(++di.begin(), 500);
print("在迭代器指向的位置添加节点后:",di);
di.erase(di.begin());
print("删除迭代器指向的节点后:", di);
typedef deque<int>::iterator IT;
IT fit = find(di.begin(), di.end(), 10);
if(fit!=di.end())
di.erase(fit);
print("找到元素10并删除后:",di);
sort(di.begin(), di.end());
print("排序后:", di);
CMP<int> cmp;
sort(di.begin(), di.end(), cmp);
print("比较器排序后:", di);
return 0;
}
3、列表(list)
- 1)唯一化
void unique(void);//将连续重复出现的元素唯一化
- 2)排序:
列表的sort()是成员函数
,并且是全局排序不能提供排序的范围
void sort(void);//通过<比较大小
template<class LESS>void sort(LESS less);//通过比较器比较大小
- 3)拆分:将参数列表中的部分或全部元素
剪切
到调用列表中,并将剪切到的元素放到调用列表
传入的迭代器前面
的位置
//剪切被剪切列表lst的全部元素到指定的列表pos位置前
template<class IT>void splice(IT pos, list& lst);
//剪切被剪切列表lst的指定位置的元素del到指定列表pos位置前
template<class IT>void splice(IT pos, list& lst, IT del);
//剪切被剪切列表lst的部分元素(从begin到end的之间的元素,注意是左闭右开区间[begin, end))到指定的列表pos位置前
template<class IT>void splice(IT pos, list& lst, IT begin, IT end);
eg:04list.cpp
include <iostream>
#include <list>
using namespace std;
template<class T>
void print(string const& str, list<T>& l){
cout << str << endl;
typedef typename list<T>::iterator IT;
for(IT it=l.begin(); it!=l.end(); ++it){
cout << *it << ' ';
}
cout << endl << "----------------" << endl;
}
template<class T>class CMP
{
public:
bool operator()(T const& a, T const& b)const{
return a > b;
}
};
int main(void)
{
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();
print("排序后:", ls);
CMP<int> cmp;
ls.sort(cmp);
print("比较器排序后:", ls);
list<int> lst;//作为参数列表
ls.push_back(1000);
ls.push_back(2000);
ls.push_back(3000);
ls.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;
}
4、堆栈(stack)
- 1)定义形式:
stack<元素类型,[底层容器类型]>堆栈对象(构造实参表)
- 2)底层容器:vector/deque(默认)/list,可以用自己实现容器作为堆栈的底层容器
- 3)成员函数(自身成员函数->底层容器成员函数)
- push->push_back
- pop->pop_back
- top->back
- size->size
- empty->empty
- clear->clear
eg:05stack.cpp
#include <iostream>
#include <stack>
#include <vector>
#include <deque>
#include <list>
using namespace std;
int main(void)
{
//stack<int, vector<int> > s;
//stack<int, deque<int> > s;
stack<int, list<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.top() << endl;
s.pop();
}
return 0;
}
5、队列(queue)
- 1)定义形式:
queue<元素类型,[底层容器类型]>队列队形(构造实参表)
- 2)底层容器:deque(默认)/list,双端都可添加删除,所以向量不行
- 3)成员函数(自身成员函数->底层容器成员函数)
- push->push_back
- pop->pop_front
- back->back
- front->front
- size->size
- empty->empty
- clear->clear
eg:06queue.cpp
#include <iostream>
#include <queue>
#include <deque>
#include <list>
#include <vector>
using namespace std;
int main(void)
{
queue<int> q;
//queue<int, vector<int> > q;//vector不能作为队列的底层容器
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
q.push(6);
while(!q.empty()){
cout << q.front() << endl;
q.pop();
}
return 0;
}
6、优先队列(priority_queue)
- 1)定义形式:
priority_queue<元素类型,[底层容器类型],[比较器类型]>优先队列对象(构造实参表)
- 2)底层容器:deque(默认)/vector,支持随即迭代,不能使用list
- 3)注意事项:优者先出,默认以大者为优也可以通过比较器定制(比较器必须是类),如果没有比较器默认内部使用
<
运算符。而且优者先出并不是出队时挑选优者,而是进队列时就保证有序。 - 4)成员函数:push/pop/top/empty/size/clear
eg:07priorityqueue.cpp’
#include <iostream>
#include <queue>
#include <list>
#include <vector>
using namespace std;
class CMP
{
public:
bool operator()(int const& a, int const& b)const{
return a > b;
}
};
int main(void)
{
priority_queue<int, vector<int>, CMP> pq;
pq.push(5);
pq.push(9);
pq.push(2);
pq.push(8);
pq.push(1);
pq.push(6);
pq.push(3);
pq.push(7);
pq.push(0);
while(!pq.empty()){
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
7、映射(map)
- 1)定义形式:
map<键类型,值类型>映射对象;
- 2)逻辑模型:对应模型 键(信息索引) 值(信息内容)对,主要用于信息检索,性能可以达到对数级(
O(logN)
),类似二分法 - 3)物理模型:平衡有序二叉树有名红黑树(防止单联只:就变成链表了,检索就成了线性级)
- 4)键必须唯一
- 5)迭代过程实际上是关于键的中序遍历(L D R),键的升序
- 6)存储单位是由键和值组成的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对象的指针。
- 7)映射中的键是只读的
- 8)检索性能好(二分法)构建和修改性能较差,适用于结构稳定,但是需要频繁检索的操作。
- 9)支持
下标
运算,用键作为下标,得到队形的值得引用,如果所给出的键不存在,增加一个节点,返回其值的引用。 - 10)成员函数
insert(pair<FIRST,SECOND>(key, value))
insert(make_pair(key,value))
迭代器=find(key)
失败返回终止迭代(并非全局函数而是成员函数)
eg:08map.cpp
#include <iostream>
#include <map>
using namespace std;
class Candidate
{
public:
Candidate(string const& name=""):m_name(name), m_vote(0){}
string GetName(void){
return m_name;
}
int GetVote(void){
return m_vote;
}
void SetVote(void){
++m_vote;
}
private:
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.GetName() << ' ';
}
cout << endl;
}
int main(void)
{
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.SetVote();
}
IT win = m.begin();
for(IT it=m.begin(); it!=m.end(); ++it){
cout << (*it).second.GetName() << ":" << (*it).second.GetVote() << endl;
if((*it).second.GetVote() > (*win).second.GetVote())
win = it;
}
cout << "恭喜:" << (*win).second.GetName() << "成功当选*****" << endl;
return 0;
}
8、多重映射(mutimap)
允许键值重复的映射,表示一对多的逻辑关系,不支持下标运算符
eg:09multimap.cpp
#include <iostream>
#include <map>
using namespace std;
int main(void)
{
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));
cout << "节点个数:" << m.size() << endl;
typedef multimap<string, int>::iterator IT;
for(IT it=m.begin(); it!=m.end(); it++){
cout << (*it).first << ":" << (*it).second << ' ';
}
cout << endl;
return 0;
}
9、集合(set)
没有值只要键的映射,和向量等基本容器相比最大的优势就是排重
eg:10set.cpp
#include <iostream>
#include <set>
using namespace std;
int main(void)
{
set<char> s;
s.insert('a');
s.insert('b');
s.insert('c');
s.insert('a');
s.insert('b');
s.insert('c');
cout << "节点个数:" << s.size() << endl;
typedef set<char>::iterator IT;
for(IT it=s.begin(); it!=s.end(); ++it){
cout << *it << ' ';
}
cout << endl;
return 0;
}
10、多重集合(multiset)
没有值只有键的多重映射
eg:multiset.cpp
#include <iostream>
#include <set>
using namespace std;
int main(void)
{
multiset<char> s;
s.insert('a');
s.insert('b');
s.insert('c');
s.insert('a');
s.insert('b');
s.insert('c');
cout << "节点个数:" << s.size() << endl;
typedef multiset<char>::iterator IT;
for(IT it=s.begin(); it!=s.end(); ++it){
cout << *it << ' ';
}
cout << endl;
return 0;
}