1 STL
1.1 STL的诞生
C++面向对象和泛型编程思想,目的是提高复用性。为了建立数据结构和算法的一套标注,STL孕育而生。
1.2 STL基本概念
- STL(Standard Template Library 标准模版库)
- STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器进行连接
- STL几乎所有代码都采用了模版类和模版函数
1.3 STL六大部件
STL大体分为六大组件:容器、算法、迭代器、仿函数、适配器、空间配置器
- 容器:各种数据结构,如:vector、list、deque、set、map等,用来存放数据
- 算法:各种常用算法,如:sort、find、copy、foreach等
- 迭代器:扮演了容器和算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器、仿函数或迭代器接口的内容
- 空间配置器:负责空间的配置与管理
1.4 容器概念
常用的数据结构:数组、链表、树、栈、队列、集合、映射表等。
这些容器分为序列式容器和关联式容器两种:
- 序列式容器:强调值的排序,序列式容器中的每个元素都有固定的位置
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
1.5 算法概念
算法分为:质变算法和非质变算法
质变算法:指运算过程中会更改区间的元素内容。例如:拷贝、替换、删除等
非质变算法:指运算过程中不会更改区间内的元素内容。例如:查找、计数、遍历等。
1.6 迭代器概念
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己的专属迭代器,迭代器类似指针。
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,++、==、!= |
双向迭代器 | 读写操作,并能向前和向后推进迭代器 | 读写,++、-- |
随机访问迭代器 | 读写操作,可以跳跃的方式访问任意数据 | 读写,++、--、[n]、-n、<、<=、>、>= |
2 初识容器算法迭代器
2.1 vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector::iterator
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void printfVector(int val){
cout << val << " ";
}
/// 实现vector存放内置数据类型
void SaveInner(){
//声明容器vector对象
vector<int> zhangsan;
//往容器内插入数据
zhangsan.push_back(10);
zhangsan.push_back(20);
zhangsan.push_back(30);
//通过迭代器访问容器中的数据
//起始迭代器:begin()迭代器指向容器第一个元素
vector<int>::iterator iBegin = zhangsan.begin();
//结束迭代器:end()迭代器指向容器最后一个元素的下一个位置
vector<int>::iterator iEnd = zhangsan.end();
//方式一:遍历输出迭代器中所有内容
while (iBegin != iEnd) {
cout << *iBegin << " ";
iBegin++;
}
cout << endl;
//方式二:遍历输出迭代器中所有内容
for(vector<int>::iterator it = zhangsan.begin();it != zhangsan.end();it++){
cout << *it << " ";
}
cout << endl;
//方式三:遍历输出迭代器中所有内容,for_each函数包含在algorithm头文件中
//参数一:起始迭代器;参数二:结束迭代器;参数三:自定义处理函数
for_each(zhangsan.begin(), zhangsan.end(), printfVector);
cout << endl;
}
2.2 vector存放自定义数据类型
实现vector存放自定义数据类型
///实现vector存放自定义数据类型
void SaveOuter(){
vector<Person> student;
student.push_back(Person("张三",22));
student.push_back(Person("李四",19));
student.push_back(Person("王五",32));
vector<Person>::iterator iBegin = student.begin();
vector<Person>::iterator iEnd = student.end();
//方式一
while(iBegin != iEnd){
cout << "name=" << iBegin->name << " " << "age=" << iBegin->age << endl;
iBegin++;
}
//方式二
for(vector<Person>::iterator it = student.begin(); it != student.end(); it++){
//下述两种方式都可以
cout << "name=" << it->name << " " << "age=" << it->age << endl;
//cout << "name=" << (*it).name << " " << "age=" << (*it).age << endl;
}
//方式三
for_each(student.begin(), student.end(), printfOuter);
}
实现vector存放自定义指针数据类型
下述声明的是Person 类型,当通过(it)取出当前vector内容,实际获取的是 Person *数据类型,所以后面接了一个->成员访问符;取出的就是声明的数据类型
///存放指针
void SaveOuterPointer(){
vector<Person*> student;
Person zhangsan("张三",22);
Person lisi("李四",19);
Person wangwu("王五",32);
student.push_back(&zhangsan);
student.push_back(&lisi);
student.push_back(&wangwu);
//其他两种方式,与上面类似
for(vector<Person*>::iterator it = student.begin(); it != student.end();it++){
cout << "name=" << (*it)->name << " " << "age=" << (*it)->age << endl;
}
}
2.3 Vector容器嵌套
//遍历大vector容器内容
void printfVector(vector<int> v){
//然后在通过一个for_each和lambda语法打印每个子vector中内容
for_each(v.begin(), v.end(), [](int value) {
cout << value << " ";
});
cout << endl;
}
//Vector容器嵌套
//Vector容器中在嵌套一个Vector
void NestVector(){
//大vector容器
vector<vector<int>> bigVector;
//子vector容器
vector<int> vector1;
vector<int> vector2;
//初始化子vector容器初始值
for(int i=0; i<3; i++){
vector1.push_back(i);
vector2.push_back(i+1);
}
//将子vector容器使用尾插法插入大vector容器
bigVector.push_back(vector1);
bigVector.push_back(vector2);
//使用alogrithm库中的for_each算法遍历大vector容器
for_each(bigVector.begin(), bigVector.end(), printfVector);
}
3 string容器
本质:
- string是C++风格的字符串,而string本质是一个类
string和char *的区别:
- char *是一个指针
- string是一个类,类内部封装了char 管理这个字符串,是一个char 型容器
特点:
- string类内部封装了大量成员函数
- 例如:查找find、拷贝copy、删除delete、替换replace、插入insert
- string管理char *所分配的内存,不用担心复制越界和取值等,由类内部负责
3.1 string构造函数
string构造函数原型:
- string(); //创建一个空的字符串;默认构造
- string(const char *s); //使用字符串s进行初始化;有参构造
- string(const string &str); //使用一个string对象初始化另一个string对象;拷贝构造
- string(int n,char c); //使用n个字符c初始化c
void stringTest01(){
//默认构造函数
string zhangsan;
//有参构造
const char *s = "李四";
string lisi(s);
cout << lisi << endl;
//拷贝构造函数
string wangwu(lisi);
cout << wangwu << endl;
string zhaoer(5,'a');
cout << zhaoer << endl;
}
3.2 string赋值操作
赋值函数原型:
- string& operator =(const char s); //char s类型赋值给当前字符串
- string& operator =(const string &s); //字符串s赋值给当前字符串
- string& operator =(char c); //字符c赋值给当前字符串
- string& assign (const char *s);//字符串s赋值给当前字符串
- string& assign (const char *s,int n);//字符串s的前n个字符赋值给当前字符串
- string& assign (const string &s);//字符串s赋值给当前字符串
- string& assign (int n,char c); //将n个字符c赋值给当前字符串
void stringTest02(){
//原型:string& operator =(const char *s);
const char *hello = "hello";
string str1 = hello;
cout << str1 << endl;
//原型:string& operator =(const string &s);
string str2 = str1;
cout << str2 << endl;
//原型:string& operator =(char c);
char c = 'a';
string str3;
str3 = c;
cout << str3 << endl;
//原型:string& assign (const char *s);
string str4;
str4.assign(hello);
cout << str4 << endl;
//原型:string& assign (const char *s,int n);
string str5;
str5.assign(hello,2);
cout << str5 << endl;
//原型:string& assign (const string &s);
string str6;
str6.assign(str5);
cout << str6 << endl;
//原型:string& assign (int n,char c);
string str7;
str7.assign(5,c);
cout << str7 << endl;
}
3.3 string字符串拼接
拼接函数原型:
- string& operator +=(const char *s);
- string& operator +=(const char c);
- string& operator +=(const string &s);
- string& append(const char *s); //将字符串s拼接到当前字符串末尾
- string& append(const string &s,int n); //将字符串s的前n个字符拼接到当前字符串末尾
- string& append(const string &s,int pos,int n); //将字符串s第pos位置开始之后的n个字符拼接到当前字符串末尾
void stringTest03(){
const char *hello = "hello";
const char c = 'c';
//原型:string& operator +=(const char *s);
string str1("lisi");
str1 += hello;
cout << str1 << endl;
//原型:string& operator +=(const char c);
string str2("wangwu");
str2 += c;
cout << str2 << endl;
//原型:string& operator +=(const string &s);
string str3("zhaoer");
str3 += str2;
cout << str3 << endl;
//原型:string& append(const char *s);
string str4("start");
str4.append(hello);
cout << str4 << endl;
//原型:string& append(const string &s,int n);
string str5("start");
str5.append(hello,2);
cout << str5 << endl;
//原型:string& append(const string &s,int pos,int n);
string str6("start");
str6.append(hello,2,2);
cout << str6 << endl; //startll
}
3.4 string查找和替换
描述:
- 查找:查找指定字符串是否存在
- 在指定的位置替换字符串
函数原型:
- int find(const string &str,int pos = 0) const; //查找str第一次出现位置,从pos位置开始查找
- int find(const char *s,int pos = 0) const; //查找r第一次出现位置,从pos位置开始查找
- int find(const char *s,int pos,int n) const; //从pos位置查找字符串s的前n个字符第一次出现的位置
- int find(const char c,int pos = 0) const; //查找字符c第一次出现位置,从pos位置开始查找
- int rfind(const string &str,int pos = npos) const; //查找str最后一次出现位置,从pos位置开始查找
- int rfind(const char *s,int pos = npos) const; //查找s最后一次出现位置,从pos位置开始查找
- int rfind(const char *s,int pos,int n) const; //从pos位置查找字符串s的前n个字符最后一次出现的位置
- int rfind(const char c,int pos = 0) const; //查找字符c最后一次出现位置,从pos位置开始查找
- int replace(int pos,int n,const string &str); //将当前字符串的第pos位置开始的n个字符替换成字符串str
- int replace(int pos,int n,const char *s);//将当前字符串的第pos位置开始的n个字符替换成字符串s
void stringTest04(){
string str = "hello c++,I'm liszt";
//原型:int find(const string &str,int pos = 0) const;
string str1 = "c++";
int pos1 = str.find(str1);
cout << pos1 << endl;//6
//原型:int find(const char *s,int pos = 0) const;
const char *liszt = "liszt";
int pos2 = str.find(liszt);
cout << pos2 << endl;//14
//原型:int find(const char *s,int pos,int n) const;
int pos3 = str.find(liszt,0,1);
cout << pos3 << endl;//2
//原型:int replace(int pos,int n,const string &str);
string str2 = "franz";
str.replace(14, str2.size(), str2);//hello c++,I'm franz
cout <<"替换:" << str << endl;
//原型:int replace(int pos,int n,const char *s);
const char *franz = "hhhhh";
str.replace(0, strlen(franz), franz);
cout <<"替换:" << str << endl;//hhhhh c++,I'm franz
}
3.5 string字符串比较
比较方式:
- 字符串比较是按字符ASIIC码进行对比
返回结果:
- 相等,返回0
- 大于,返回第一个相异字符的ASCII码值之差(正数)
- 小于,返回第一个相异字符的ASCII码值之差(负数)
函数原型:
- int compare(const string &str) const;
- int compare(const char *s) const;
void stringTest05(){
string str1("bda");
string str2("bx");
int result1 = str1.compare(str2);
cout << "result=" << result1 << endl; //-20,d的ASCII码值减x的ASCII码值
const char *s = "ba";
int result2 = str1.compare(s);
cout << "result=" << result2 << endl; //3,d的ASCII码值减a的ASCII码值
int result3 = str1.compare("bda");
cout << "result=" << result3 << endl; //0
}
3.6 string字符存取
存取方式包括两种:
3.7 string插入和删除
函数原型:
- string& insert(int pos,const char *s);
- string& insert(int pos,const string &s);
- string& insert(int pos,int n,char c);
- string& erase(int pos,int n = npos); //删除从pos开始的n个字符
void stringTest06(){
string str = "hello world!";
string insertContent = "c++";
//原型:string& insert(int pos,const char *s);
str.insert(0,insertContent);
cout << str << endl;
//原型:string& insert(int pos,int n,char c);
str.insert(8, 1,'x');
cout << str << endl;
//原型:string& erase(int pos,int n = npos);
str.erase(8,1);
cout << str << endl;
}
3.8 string子串
描述:从字符串中截取一部分子串并返回
函数原型:
- string substr(int pos = 0,int n = npos); //返回从字符串第pos位置开始的n个字符
void stringTest07(){
string str = "hello world!";
string sub = str.substr(6,5);
cout << sub << endl;//world
}
4 vector容器
4.1 vector概念
vector与数组非常类似,称为单端数组
与普通数组的区别:
- 不同之处在于数组是静态空间,而vector可以动态扩展
- 动态扩展:并不是在原空间之间继续扩展,而且寻找一片更大的内存空间,然后将原数据拷贝到新空间内,在释放原空间
- vector容器迭代器是支持随机访问的迭代器
函数原型:
- vector v;
- vector v1(v.begin(),v.end()); //[begin,end),左闭右开区间内的元素拷贝给本身
- vector v1(n,elem); //将n个elem拷贝给本身
- vector v1(const vector &v); //拷贝构造函数
void vectorTest1(){
//默认构造,原型:vector<T> v
vector<int> v1;
for(int i=0; i<5; i++) v1.push_back(i);
printfVectorInt(v1);
//通过区间内容进行构造,原型:vector(v.begin(),v.end());
vector<int> v2(v1.begin(),v1.end());
printfVectorInt(v2);
//原型:vector(n,elem);
vector<int> v3(5,v1[0]);
printfVectorInt(v3);
//拷贝构造,原型:vector v1(const vector &v);
vector<int> v4(v3);
printfVectorInt(v4);
}
4.2 vector赋值操作
函数原型:
- vector& operator =(const vector &v);
- assign(beg,end);//左闭右开区间内元素拷贝给自身
- assign(n,elem); //将n个elem拷贝复制给本身
void vectorTest2(){
vector<int> v1;
for(int i=0; i<5; i++) v1.push_back(i);
printfVectorInt(v1);
//原型:vector& operator =(const vector &v);
vector<int> v2 = v1;
printfVectorInt(v2);
//原型:assign(beg,end);
vector<int> v3;
v3.assign(v2.begin(), v2.end());
printfVectorInt(v3);
//原型:assign(n,elem);
vector<int> v4;
v4.assign(5,v3[0]);
printfVectorInt(v4);
}
4.3 vector容量和大小
函数原型:
- empty(); //判断容器是否为空
- capacity(); //容器容量
- size(); //返回容器中元素个数
- resize(int num); //重新指定容器长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器的长度的元素被删除;
- resize(int num,elem); //重新指定容器长度为num,若容器变长,则以elem填充新位置;如果容器变短,则末尾超出容器的长度的元素被删除;
void vectorTest3(){
vector<int> v1;
for(int i=0; i<5; i++) v1.push_back(i);
printfVectorInt(v1);
//判断vector是否为空
if(v1.empty()){
cout << "vector为空" << endl;
}else{
cout << "vector容量=" << v1.capacity() << endl;
cout << "vector元素个数=" << v1.size() << endl;
}
//容器变长,新位置插入默认值0,因为是int型
v1.resize(7);
printfVectorInt(v1);
//容器变短,尾部超出vector长度,则删除默认元素
v1.resize(3);
printfVectorInt(v1);
//容器变长,并新位置用插入5
v1.resize(8,5);
printfVectorInt(v1);
}
4.4 vector插入和删除
函数原型:
- push_back(elem); //在尾部插入
- pop_back(); //删除最后一个元素
- insert(const_iterator pos,ele); //迭代器指向的位置pos插入元素ele,其余位置向后移动,空出一个位置
- insert(const_iterator pos,int count,ele); //迭代器指向的位置pos插入n个元素ele
- erase(const_iterator pos); //删除迭代器指向的pos位置元素
- erase(const_iterator start,const_iterator end);//删除迭代器[start,end)区域的元素
- clear(); //删除容器所有元素
4.5 vector数据存取
函数原型:
- at(int index);
- operator[index];
- front(); //返回容器中第一个数据元素
- back(); //返回容器中最好一个数据元素
4.6 vector互换容器
函刷原型:
- swap(vec); //将vec与本身的元素进行互换
当一个vector容器初始化的时候容量很大,但通过需求使用resize()将原空间大幅度缩减,但是容量并不会减少,就会产生空间浪费,此时可以使用swap创建一个匿名对象交换vector容器,交换后的容量将会与resize()缩减后的容量匹配;因为匿名对象处理完之后就会被回收,所以不必担心内存泄露。
vector (v).swap(v);
4.7 vector预留空间
描述:减少vector在动态扩展容量时的扩展次数
函数原型:
- reserve(int len); //容器预留len个元素长度,预留位置不能初始化,元素不能被访问
如果事先知道了需要的容量并且较大,则可以通过reserve进行预留空间
防止多次动态扩展空间,通过reserve预留空间之后,开辟次数则变为一次(量级匹配)
void vectorTest6(){
vector<int> v1;
v1.reserve(100);
int *p = NULL;
int count = 0;
for(int i=0; i<100; i++) {
v1.push_back(i);
if(p != &v1[0]){
p = &v1[0];
count++;
}
}
cout << "开辟了" << count << "次空间" << endl;
}
5 deque容器
5.1 deque容器基本概念
功能:双端数组,可以对头端进行插入删除操作
deque与vector的区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低
- deque相对而言,对头部的插入删除速度比vector快
- vector访问元素速度会比deque快,与其内部实现有关
deque内部工作原理:
deque内部有一个中控器,维护每段缓冲区的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
- deque容器的迭代器支持随机访问
5.2 deque构造函数
函数原型:
- deque deq;
- deque(beg,end);
- deque(n,elem);
- deque(const deque &deq);
void dequeTest1(){
deque<int> deq1;
//从头部添加数据
for(int i=0; i<5;i++){
deq1.push_front(i);
}
//从尾部添加数据
for(int i=5; i<10;i++){
deq1.push_back(i);
}
printf(deq1);//4,3,2,1,0,5,6,7,8,9,
//区间元素赋值给deque容器本身
deque<int> deq2(deq1.begin(),deq1.end());
printf(deq2);//4,3,2,1,0,5,6,7,8,9,
deque<int> deq3(deq1.size(),deq1[0]);
printf(deq3);//4,4,4,4,4,4,4,4,4,4,
//拷贝构造函数
deque<int> deq4(deq3);
printf(deq4);//4,4,4,4,4,4,4,4,4,4,
}
5.3 deque赋值操作
函数原型:
- deque operator =(const deque &deq);
- assign(beg,end);
- assign(n,elem);
5.4 deque大小操作
函数原型:
- deque.empty();
- deque.size();
- deque.resize(num);//重新指定容器长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器的长度的元素被删除;
- deqeue.resize(num,elem);
5.5 deque插入和删除
函数原型:
两端插入操作:
push_back(elem);//尾法
push_front(elem);//头插
pop_back();//尾删
pop_front();//头删
指定位置操作:
insert(pos,elem);
insert(pos,n,elem);
insert(pos,beg,end);
clear();
erase(beg,end);
erase(pos);
void dequeTest2(){
deque<int> deq1;
//从头部添加数据
for(int i=5; i>0;i--){
deq1.push_front(i);
}
//从尾部添加数据
for(int i=6; i<=10;i++){
deq1.push_back(i);
}
printf(deq1);//1,2,3,4,5,6,7,8,9,10,
//头删
deq1.pop_front();
printf(deq1);//2,3,4,5,6,7,8,9,10,
//尾删
deq1.pop_back();
printf(deq1);//2,3,4,5,6,7,8,9,
//指定位置插入
deq1.insert(deq1.begin(), 1);
printf(deq1);//1,2,3,4,5,6,7,8,9,
//在指定位置插入n次element元素
deq1.insert(deq1.begin(),2,0);
printf(deq1);//0,0,1,2,3,4,5,6,7,8,9,
deque<int> deq2;
deq2.push_back(10);
deq2.push_back(11);
//将deq2区间内元素通过尾插到deq1中
deq1.insert(deq1.end(), deq2.begin(),deq2.end());
printf(deq1);//0,0,1,2,3,4,5,6,7,8,9,10,11,
deque<int>::iterator it = deq1.begin();
//指针向下移动一个位置
it++;
//删除指定位置元素,需要迭代器作为参数
deq1.erase(it);//删除之后,指针相当于向前移动一个位置
printf(deq1);//0,1,2,3,4,5,6,7,8,9,10,11,
//范围性删除,左闭右开
deq1.erase(it,it+5);
printf(deq1);//5,6,7,8,9,10,11,
//清空容器内元素
deq1.clear();
printf(deq1);
}
5.6 deque 数据存取
函数原型:
- at(int index);
- operator [] (int indext);
- front(); //返回容器中第一个数据元素
- back(); //返回容器中最后一个数据元素
5.7 deque排序
函数原型:
- sort(iterator beg,iterator end); //默认排序是升序
6 stack容器
6.1 stack基本概念
概念:
stack是一种先进后出(First In Last Out)的数据结构
尾部称为栈底,头部称为栈顶;
- stack是一种受限序列,只允许在栈顶进行插入和删除
- 栈内插入数据称为入栈,栈内删除数据称为出栈
6.2 stack常用接口
构造函数:
stack stack;
stack(const stack &s); //拷贝构造函数
赋值操作:
- stack& operator = (const stack &s);
数据存取:
push(elem); //栈顶插入元素
pop(); //栈顶删除元素
top(); //返回栈顶元素
大小操作:
empty(); //判断栈是否为空
size(); //返回栈的大小
7 queue容器
7.1 queue 基本概念
概念:
- queue是一种先进先出(First In First Out)的数据结构,允许在两端进行操作
- queue只允许在队尾进行插入,队头进行删除
- 队内插入数据称为入队,队内删除数据称为出队
7.2 queue 常用接口
构造函数:
queue que;
queue(const queue &s); //拷贝构造函数
赋值操作:
- queue& operator = (const queue &q);
数据存取:
push(elem); //队尾插入元素
pop(); //队头删除元素
back(); //返回队内第一个元素元素
- top(); //返回队内最后一个元素元素
大小操作:
empty(); //判断队是否为空
size(); //返回队的大小
8 list容器
8.1 list基本概念
描述:
- 使用链式存储
- 物理存储单元非连续,通过额外的指针记录元素之间的关系
- 一个数据结点由数据域和指针域组成
- STL的链表是一个双向循环链表
- 链表的迭代器属于双向迭代器
插入和删除操作不会造成原有list迭代器的实效(在list中删除一个数据记录,非当前迭代器所指位置,则不会对迭代器造成影响),这在vector不成立
优点:
- 采用动态存储分配,不会造成内存的浪费和溢出
- 链表对插入和删除操作十分便利
缺点:
- 空间(指针域)和时间(遍历)额外耗费较大
8.2 list构造函数
函数原型:
- list list;
- list;
- list(n,elem);
- list(const list &l);
8.3 list赋值和交换
函数原型:
- assign(beg,end);
- assign(n,elem);
- list& operator = (const list &l);
- swap(list);
8.4 list大小操作
函数原型:
- size();
- empty();
- resize(num);
- resize(num,elem);
8.5 list插入和删除
函数原型:
两端插入操作:
push_back(elem);//尾法
pop_back();//尾删
push_front(elem);//头插
pop_front();//头删
指定位置插入操作:
- insert(pos,elem);
- insert(pos,n,elem);
- insert(pos,beg,end);
删除操作:
- clear();
- erase(beg,end);
- erase(pos);
- remove(elem); //删除容器中所有与elem匹配的元素
void listTest1(){
list<int> l;
for(int i=5; i<=10; i++) l.push_back(i);//尾插
for(int i=4; i>=0; i--) l.push_front(i);//头插
printfList(l);//0 1 2 3 4 5 6 7 8 9 10
l.pop_front();//头删
printfList(l);//1 2 3 4 5 6 7 8 9 10
l.pop_back();//尾删
printfList(l);//1 2 3 4 5 6 7 8 9
list<int>::iterator it = l.begin();
l.insert(it, 0);//在第一个元素位置插入一个0
printfList(l);//0 1 2 3 4 5 6 7 8 9
l.erase(it);//因为上述插入操作在第一个位置进行插入,完成后迭代器指针后移,所以此时删除的是第二个元素位置的内容
printfList(l);//0 2 3 4 5 6 7 8 9
l.remove(5); //删除容器内所有与elemnt匹配的元素
printfList(l);//0 2 3 4 6 7 8 9
l.clear(); //清空容器内所有元素
}
8.6 list数据存取
函数原型:
- front(); //返回容器内第一个元素
- back(); //返回容器内最后一个元素
8.7 list反转和排序
函数原型:
- reverse(); //反转链表
- sort(); //链表排序
bool compare(int a,int b){
return a>b;
}
void listTest3(){
list<int> l;
l.push_back(20);
l.push_back(10);
l.push_back(40);
l.push_back(30);
l.sort();//默认从小到大升序排序
printfList(l);
//自定义一个函数,在其中实现降序定义,例如第一个数大于第二个数为真,则为降序
l.sort(compare);
printfList(l);
}
对自定义数据类型进行排序
class Student{
string name;
int age;
public:
Student(string name,int age){
this->name = name;
this->age = age;
}
string getName(){
return this->name;
}
int getAge(){
return this->age;
}
};
void printfStudent(list<Student> &l){
for_each(l.begin(), l.end(), [](Student stu){
cout << "名字=" << stu.getName() << " " << "年龄=" << stu.getAge() << endl;
});
cout << endl;
}
//升序
bool StuCompare(Student &stu1,Student &stu2){
return stu1.getAge() < stu2.getAge();
}
void listTest4(){
list<Student> stus;
stus.push_back(Student("张三",20));
stus.push_back(Student("李四",40));
stus.push_back(Student("王五",10));
stus.push_back(Student("赵二",30));
stus.sort(StuCompare);
printfStudent(stus);
}
9 set 容器和multiset
9.1 简述
基本概念:
- 所有元素都会在插入时自动被排序
- set/multiset属于关联式容器,底层结构是用二叉树实现
set/multiset的区别:
set不允许容器中有重复的元素,如果插入重复的元素,set会默认忽视此元素
multiset允许容器中有重复的元素
9.2 set构造和赋值
构造:
- set st;
- set(const set &st);
赋值:
- set& operator =(const set &st);
void setTest1(){ //默认构造函数 set<int> s1; for(int i=0; i<50; i+=10){ s1.insert(i); } printfSet(s1);//0 10 20 30 40 ///set容器会对插入的元素进行默认排序且不允许出现重复元素 ///如果插入的重复元素,set会自动忽视这个元素,则不进行插入 set<int> s2; s2.insert(20); s2.insert(0); s2.insert(10); s2.insert(40); s2.insert(30); printfSet(s2);//0 10 20 30 40 //拷贝构造函数 set<int> s3(s2); printfSet(s3);//0 10 20 30 40 //赋值 set<int> s4 = s3; printfSet(s3);//0 10 20 30 40 } int main(int argc, const char * argv[]) { setTest1(); return 0; }
9.3 set大小和交换
函数原型:
- size();
- empty();
- swap(st);
9.4 set插入和删除
函数原型:
- insert(elem);
- clear();
- erase(pos);
- erase(beg,end);
- erase(elem);
9.5 set查找和统计
- find(key); //若key存在,则返回该key的元素迭代器;反之,返回end()迭代器
- count(key);//统计key的元素个数,对于set容易不允许插入重复元素,故结果只在0和1之间
9.6 pair 对组容器
描述:
- 成对出现的数据,利用对组可以返回两个数据
创建方式:
- pari p(value1,value2);
- pair p = make_pair(value1,value2);
9.7 set容器排序
描述:
set默认插入排序规则是从小到大,可以通过仿函数进行改变排序规则
class MyCompare{
public:
bool operator()(int a,int b){
return a>b;
}
};
...
void setTest2(){
set<int> s1;
//默认从小到大
for(int i=0; i<50; i+=10){
s1.insert(i);
}
printfSet(s1);
//通过仿函数,改变排序规则,从大到小
set<int,MyCompare> s2;
for(int i=0; i<50; i+=10){
s2.insert(i);
}
printfSet(s2);
}
9.8 改变自定义数据类型set排序规则
通过将自定义数据类型Student的进行排序,按其成员字段age从大到小进行降序排序
class Student{
public:
string name;
int age;
Student(string name,int age){
this->name = name;
this->age = age;
}
};
...
class MyCompare{
public:
bool operator()(Student a,Student b){
return a.age > b.age;
}
};
...
void setTest3(){
set<Student,MyCompare> s1;
s1.insert(Student("张三",20));
s1.insert(Student("李四",10));
s1.insert(Student("王五",40));
s1.insert(Student("赵二",30));
printfSet(s1);
}
10 map和multimap容器
10.1 map基本概念
描述:
- map的所以元素都是pair
- pair中第一个元素为key-键值,起到索引作用;第二个元素为value-实值
- 所有元素都会根据元素的键值自动排序
- map/multimap属于关联式容器,底层结果使用二叉树实现
优点:
- 根据快速根据key值找到value值
map/multimap区别
- map不允许容器中重复的key值元素
- multimap允许容器中重复的key值元素
10.2 map构造和赋值
函数原型:
构造:
map mp;
map(const map &mp);
赋值
- map & operator = (const map &mp);
10.3 map大小和交换
函数原型:
- size();
- empty();
- swap(st);
10.4 map 插入和删除
函数原型:
- insert(elem);
- clear();
- erase(pos);
- erase(begin,end);
- erase(key);
void printfMap(map<string,int> &m){
for_each(m.begin(), m.end(), [](pair<string, int> value){
cout << "name=" << value.first << " " << "age=" << value.second << endl;
});
}
void mapTest1(){
map<string,int> map1;
///下列四种方式都可以实现插入map数据
///默认通过key值进行升序排序
map1.insert(pair<string, int>("peter",12));
map1.insert(make_pair("jerry", 24));
map1.insert(map<string,int>::value_type("liszt",30));
//若执行map1["www"];没有找到"www"所管理的值,则会默认创建一个以“www”为key值的默认数据类型值的数据
map1["franz"] = 7;
printfMap(map1);
//通过key值进行删除
map1.erase("jerry");
printfMap(map1);
}
10.5 map查找和统计
函数原型:
- find(key);
- count(key);
10.6 map排序
#include <iostream>
#include <map>
#include <algorithm>
#include <string>
using namespace std;
class MyCompare{
public:
bool operator()(string a, string b){
return a > b;
}
};
void printfMap(map<string,int,MyCompare> &m){
for_each(m.begin(), m.end(), [](const pair<string, int> &value){
cout << "name=" << value.first << " " << "age=" << value.second << endl;
});
}
void mapTest2(){
map<string,int,MyCompare> map1;
map1.insert(make_pair("jack", 14));
map1.insert(make_pair("jerry", 24));
map1.insert(make_pair("liszt", 4));
printfMap(map1);
}
int main(int argc, const char * argv[]) {
mapTest2();
return 0;
}
11 函数对象
11.1 函数对象
概念:
- 重载函数调用操作符的类,其对象称为函数对象
- 函数对象使用重载的()时,行为类似函数调用,也称为仿函数
- 函数对象(仿函数)是一个类,不是一个函数
特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
class MyAdd{
public:
int operator() (int a, int b){
return a+b;
}
};
///函数对象可以作为参数传递
void doAdd(MyAdd &add){
cout << "result=" << add(20,20) << endl;
}
void test1(){
//构建函数对象
MyAdd add;
cout << "result=" << add(10,20) << endl;
doAdd(add);
}
11.2 谓词
概念:
- 返回bool类型的仿函数称为谓词
- 如果operator()接受一个参数,称为一元谓词
- 如果operator()接受俩个参数,称为二元谓词
///一元谓词
bool FindNumber(int a){
return a > 5;
}
void test2(){
vector<int> v1;
for(int i=0; i<10; i++){
v1.push_back(i);
}
///find_if如果条件成立,则返回元素迭代器位置;未找到,则返回end()迭代器
vector<int>::iterator it = find_if(v1.begin(), v1.end(),FindNumber);
if(it == v1.end()){
cout << "没有大于5的数字" << endl;
}else{
cout << "result=" << *it << endl;
}
}
11.3 内建函数对象
分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
- 引用时,需要导入#include
11.3.1 算术仿函数
功能描述:
- 实现四则运算
- 其中negate是一元运算,其他都是二元运算
仿函数原型:
- template T plus //加法仿函数
- template T minus //减法仿函数
- template T multiplies //乘法仿函数
- template T divides //除法仿函数
- template T modulus //取模仿函数
- template T negate //取反仿函数
///取反反函数
void test3(){
negate<int> n;
cout << "50的相反数=" << n(50) << endl;
}
///加法仿函数
void test4(){
plus<int> p1;
cout << "10 + 20 = " << p1(10,20) << endl;
}
11.3.2 关系仿函数
仿函数原型:
- template bool equal //等于
- template bool not_equal //不等于
- template bool greater //大于
- template bool greater_equal //大于等于
- template bool less //小于等于
- template bool less_equal //小于等于
void test5(){
vector<int> v1;
v1.push_back(10);
v1.push_back(5);
v1.push_back(20);
//使用内置的greater仿函数,作为使用降序
sort(v1.begin(), v1.end(), greater<int>());
for_each(v1.begin(), v1.end(), [](int num){
cout << num << " ";
});
cout << endl;
}
11.3.3 逻辑仿函数
仿函数原型:
- template bool logical_and //逻辑与
- template bool logical_or //逻辑或
- template bool logical_not //逻辑非
void test01()
{
vector<bool>v;
v.push_back(true);
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//利用逻辑非 将容器v搬运到 容器v2中,并执行取反操作
vector<bool>v1;
v1.resize(v.size());
// transform 必须先开辟空间指定大小v1.resize(v.size()); 在执行搬运
transform(v.begin(), v.end(), v1.begin(), logical_not<bool>());
for (vector<bool>::iterator it = v1.begin(); it != v1.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
12 常用算法
概述:
- 算法主要由头文件、、组成
- 是STL中头文包含最广的之一,涉及范围包括:比较、交换、查找、遍历、复制、修改等
- 包括几个在序列上进行简单运算的模版函数
- 包括函数对象等模版类
12.1 常用遍历算法
描述:
- for_each //遍历容器
- transform //搬运容器到另一个容器中
12.1.1 for_each
函数原型:
for_each(iterator beg,iterator end,_func);
12.1.2 transform
函数原型:
- transform(iterator beg1,iterator end1,iterator beg2,_func);
class MyTransform{
public:
int operator()(int a){
return a;
}
};
void printf(vector<int> &v){
for_each(v.begin(), v.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
void test1(){
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
vector<int> v2;
//需要给目标容器开辟空间
v2.resize(v1.size());
transform(v1.begin(), v1.end(), v2.begin(),MyTransform());
printf(v2);
}
12.2 常用查找算法
描述:
- find //查找元素
- find_if //按条件查找元素
- adjacent_find //查找相邻元素
- binary_search //二分查找法
- count //统计元素个数
- count_if //按条件统计元素个数
12.2.1 find
函数原型:
- find(iterator beg,iterator end,value);
查找内置数据类型:
void test2(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
//如果找到这个元素,则返回key的迭代器位置;否则,容器的end()迭代器
vector<int>::iterator it = find(v1.begin(), v1.end(),3);
if(it != v1.end()){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
查找自定义数据类型:
class Student{
public:
string name;
int age;
Student(string name,int age){
this->name = name;
this->age = age;
}
bool operator == (const Student &stu){
return (this->name == stu.name) && (this->age == stu.age);
}
};
...
void test3(){
vector<Student> stus;
Student zhangsan("张三",20);
Student lisi("李四",30);
Student wangwu("王五",40);
stus.push_back(zhangsan);
stus.push_back(lisi);
stus.push_back(wangwu);
///对自定义数据类型进行遍历,需要重写操作法==
vector<Student>::iterator it = find(stus.begin(), stus.end(), Student("张三",20));
if(it != stus.end()){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
12.2.2 find_if
函数原型:
- find_if(iterator beg,iterator end,_pred);
条件查询内置数据类型:
///一元谓词
bool find_number(int num){
return num >= 4;
}
void test4(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
vector<int>::iterator it = find_if(v1.begin(), v1.end(), find_number);
if(it != v1.end()){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
条件查询自定义数据类型:
bool find_student(const Student &stu){
return stu.age > 30;
}
void test5(){
vector<Student> stus;
Student zhangsan("张三",20);
Student lisi("李四",30);
Student wangwu("王五",40);
stus.push_back(zhangsan);
stus.push_back(lisi);
stus.push_back(wangwu);
///对自定义数据类型进行遍历,需要重写操作法==
vector<Student>::iterator it = find_if(stus.begin(), stus.end(), find_student);
if(it != stus.end()){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
12.2.3 adjacent_find
函数原型:
- adjacent_find(iterator beg,iterator end); //查找相邻重复元素
返回第一个相邻元素的迭代器
void test6(){
vector<int> v1;
v1.push_back(1);
v1.push_back(0);
v1.push_back(1);
v1.push_back(3);//和下一个3相邻且相邻,返回次元素迭代器
v1.push_back(3);
vector<int>::iterator it = adjacent_find(v1.begin(), v1.end());
if(it != v1.end()){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
12.2.4 binary_search
函数原型:
- bool binary_find(iterator beg,iterator end,value);
需要查找序列有序,如果无序不会报逻辑错误,只是查找算法失效
void test7(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
bool result = binary_search(v1.begin(), v1.end(), 2);
if(result){
cout << "success" << endl;
}else{
cout << "failed" << endl;
}
}
12.2.5 count
函数原型:
count(iterator beg,iterator end,value); //返回容器中与value相同的元素个数
统计内置数据类型:
void test8(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
v1.push_back(4);
int num = count(v1.begin(), v1.end(),4 );
cout << "容器中存在" << num << "个与数字4相同的数据" << endl;
}
统计自定义数据类型:
class Student{
public:
string name;
int age;
Student(string name,int age){
this->name = name;
this->age = age;
}
bool operator == (const Student &stu){
return this->age == stu.age;
}
};
...
void test9(){
vector<Student> stus;
Student zhangsan("张三",20);
Student lisi("李四",30);
Student wangwu("王五",20);
stus.push_back(zhangsan);
stus.push_back(lisi);
stus.push_back(wangwu);
Student zhangsi("张三",20);
int num = count(stus.begin(), stus.end(), zhangsan);
cout << "容器中存在" << num << "个与年龄为20的人" << endl;
}
12.2.6 count_if
函数原型:
- count_if(iterator beg,iterator end,_pred);
条件统计内置数据类型:
bool count_less_3(int num){
return num < 3;
}
void test10(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
int num = count_if(v1.begin(), v1.end(), count_less_3);
cout << "小于3的数据条数有:" << num << endl;
}
条件统计自定义数据类型:
bool count_age_pass_20(const Student &stu){
return stu.age > 20;
}
void test11(){
vector<Student> stus;
Student zhangsan("张三",20);
Student lisi("李四",30);
Student wangwu("王五",20);
Student zhaoer("赵二",50);
stus.push_back(zhangsan);
stus.push_back(lisi);
stus.push_back(wangwu);
stus.push_back(zhaoer);
int num = count_if(stus.begin(), stus.end(), count_age_pass_20);
cout << "年龄大于20的数据条数有:" << num << endl;
}
12.3 常用排序算法
描述:
- sort
- random_shuffle //将指定范围你的元素随机调整次序
- merge //容器元素合并,并存储到另一容器中
- reverse //反转指定范围的元素
12.3.1 sort
函数原型:
- sort(iterator beg,iterator end,_pred);
bool sort_less(int a,int b){
return a > b;
}
void test12(){
vector<int> v1;
v1.push_back(10);
v1.push_back(50);
v1.push_back(30);
v1.push_back(20);
//默认升序
sort(v1.begin(), v1.end());
for_each(v1.begin(), v1.end(), [](int value){
cout << value << " ";
});
cout << endl;
//sort(v1.begin(), v1.end(),sort_less);
//内置降序
sort(v1.begin(), v1.end(),greater<int>());
for_each(v1.begin(), v1.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.3.2 random_shuffle
函数原型:
- random_shuffle(iterator beg,iterator end);
12.3.3 merge
函数原型:
两个容器必须是有序的,将俩个容器合并到另一容器
- merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
void test14(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
vector<int> v2;
for(int i=5;i<10;i++){
v2.push_back(i);
}
//给目标容器分配空间
vector<int> v3;
v3.resize(v1.size()+v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), v3.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.3.4 reverse
函数原型:
容器内元素反转
- reverse(iterator beg,iterator end);
void test15(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
reverse(v1.begin(), v1.end());
for_each(v1.begin(), v1.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.4 常用拷贝和替换算法
描述:
- copy //容器指定范围的元素拷贝到另一容器中
- replace //将容器内指定范围的旧元素修改为新元素
- replace_if //容器内指定范围内满足条件的元素替换为新元素
- swap //互换两个容器内的元素
12.4.1 copy
函数原型:
- copy(iterator beg,iterator end,iterator dest);
void test16(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(), v1.end(), v2.begin());
for_each(v2.begin(), v2.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.4.2 replace
函数原型:
- replace(iterator beg,iterator end,oldvalue,newvalue);
void test17(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
//将容器内容为2的元素替换为0
replace(v1.begin(), v1.end(), 2, 0);
for_each(v1.begin(), v1.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.4.3 replace_if
函数原型:
- replace_if(iterator beg,iterator end,_pred,newvalue);
bool replace_condition(int num){
return num > 2;
}
void test18(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
//将容器大于2的元素替换为0
replace_if(v1.begin(), v1.end(), replace_condition, 0);
for_each(v1.begin(), v1.end(), [](int value){
cout << value << " ";
});
cout << endl;
}
12.4.4 swap
函数原型:
- swap(container c1,container c2);
void test19(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
vector<int> v2;
for(int i=5;i<10;i++){
v2.push_back(i);
}
cout << "before swap v1:";
printf(v1);
cout << "before swap v2:";
printf(v2);
swap(v1, v2);
cout << "after swap v1:";
printf(v1);
cout << "after swap v2:";
printf(v2);
}
12.5 常用算法生成算法
描述:
- 下列算法包含于#include头文件内
- accumulate
- fill
12.5.1 accumulate
函数原型:
计算容器内元素总和
- accumulate(iterator beg,iterator end,value);
void test20(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
//最后面那个0是初始值
int sum = accumulate(v1.begin(), v1.end(), 0);
cout << "result=" << sum << endl;
}
12.5.2 fill
函数原型:
向容器内填充指定的元素
- fill(iterator beg,iterator end,value);
void test21(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(0);
}
//将容器内指定范围的元素填充为对应元素
fill(v1.begin(), v1.end(), 1);
printf(v1);
}
12.6 常用集合算法
描述:
- set_intersection //求两个容器的交集
- set_union //求两个容器的并集
- set_difference //求两个容器的差集
12.6.1 set_intersection
函数原型:
- set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
void test22(){
vector<int> v1;
for(int i=0;i<7;i++){
v1.push_back(i);
}
vector<int> v2;
for(int i=2;i<10;i++){
v2.push_back(i);
}
vector<int> v3;
int size = min(v1.size(), v2.size());
v3.resize(size);
//set_intersection会返回交集的最后一个迭代器位置
vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
///如果此处使用v3.end()作为结束迭代器,如果容器的空间大于交集元素,则会默认填充0(int),则会导致遍历容器,非交集元素0也会被输出
///使用set_intersection返回的结束迭代器为最后一个交集元素的下一个位置,则不会输出0
for_each(v3.begin(), itEnd, [](int value){
cout << value << " ";
});
cout << endl;
}
12.6.2 set_union
函数原型:
- set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);
void test23(){
vector<int> v1;
for(int i=0;i<7;i++){
v1.push_back(i);
}
vector<int> v2;
for(int i=2;i<10;i++){
v2.push_back(i);
}
vector<int> v3;
int size = max(v1.size(), v2.size());
v3.resize(size);
vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), itEnd, [](int value){
cout << value << " ";
});
cout << endl;
}
12.6.3 set_different
差集:
不是交集的部分
分v1对v2和v2对v1两种情况
- 例如v1:1 2 3 4 5. v2: 3 4 5 6 7
- 则v1对v2的差集 = 1 2
- v2对v1的差集 = 6 7
函数原型:
- set_different(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest)
void test24(){
vector<int> v1;
for(int i=1;i<6;i++){
v1.push_back(i);
} //1 2 3 4 5
vector<int> v2;
for(int i=3;i<8;i++){
v2.push_back(i);
}//3 4 5 6 7
vector<int> v3;
int size = max(v1.size(), v2.size());
v3.resize(size);
//v1对v2的差集
vector<int>::iterator itEnd1 = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
for_each(v3.begin(), itEnd1, [](int value){
cout << value << " ";
});//1 2
cout << endl;
//v2对v1的差集
vector<int>::iterator itEnd2 = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v3.begin());
for_each(v3.begin(), itEnd2, [](int value){
cout << value << " ";
});//6 7
cout << endl;
}