- 模板
通用的模具。
特点:不可以直接使用,只是一个框架; 模板的通用并不是万能的。
编程思想: 泛型编程,利用了模板。
C++提供了两种模板机制,函数模板和类模板。
1.1 函数模板:
作用:建立一个通用函数,函数返回值和形参类型不具体指定,用一个虚拟类型来代表。
template<typename T> 也可以是template<class T>
函数声明或定义,
例如:
template<typename T> //声明一个模板,告诉编译器T是一个数据类型
void mySwap(T &a,T &b){
T temp=a;
a=b;
b=temp;
}
void test01(){
int a=10,b=20;
cout<<a<<" "<<b<<endl;
//使用1 自动类型推导:必须推导出一致的数据类型T才可以使用。模板必须确定T的类型才能使用。
mySwap(a,b);
cout<<a<<" "<<b<<endl;
//使用2 显式制定类型
a=10,b=20;
cout<<a<<" "<<b<<endl;
mySwap<int>(a,b);
cout<<a<<" "<<b<<endl;
}
案例,利用函数模板封装 排序函数,选择排序 从大到小
#include<iostream>
using namespace std;
template<class T>
void mySwap(T &a,T &b){
T temp=a;
a=b;
b=temp;
}
template<class T> //声明一个模板,告诉编译器T是一个数据类型
void mySort(T arr[],int len){
for(int i=0;i<len;i++){
int max=i;
for(int j=i+1;j<len;j++){
if(arr[max]<arr[j]){
max=j;
}
}
if(max!=i){
mySwap(arr[max],arr[i]);
}
}
}
template<class T>
void myPrintArray(T arr[],int len){
for(int i=0;i<len;i++){
cout<<arr[i]<<"\t";
}
cout<<endl;
}
void test01(){
char charArr[]="badcfe";
myPrintArray(charArr,sizeof(charArr));
mySort(charArr,sizeof(charArr));
myPrintArray(charArr,sizeof(charArr));
int intArr[]={7,5,1,3,9,2,4,6,8};
int len=sizeof(intArr)/sizeof(intArr[0]);
myPrintArray(intArr,len);
mySort(intArr,len);
myPrintArray(intArr,len);
}
int main(void){
test01();
system("pause");
return 0;
}
普通函数与函数模板 区别:
普通函数 调用时 可以发送自动类型转换 (隐型类型转换)
函数模板 调用时 自动类型推导时,不发生隐型类型转换
显式制定类型时,会发生。
调用规则:
- 普通函数与函数模板都可以实现,优先使用普通函数;若普通函数没有实现只有声明,则报错。
- 如果想要调用函数模板,可以通过空模板参数列表来强制调用函数模板;myfunc<>(a,b);
- 函数模板也可以发生重载;
- 如果函数模板可以产生更好的匹配,优先调用函数模板。
局限:
不是通用的。比如对于自定义数据类型就不能运行,需要用具体化的方式做特殊实现。
解决:模板的重载,可以为特定的类型提供具体化的模板,
template<> bool myCompare(Person &p1,Person &p2){
if(p1.m_Name==p2.m_Name && p1.m_Age==p2.m_Age)
return true;
else
return false;
}
学习模板不是为了写模板,而是为了在STL能够运用系统提供的模板。
1.2 类模板
作用:建立一个通用类,类中成员 的数据类型可以不具体制定,用一个虚拟类型来代表。
template<typename T> 或template<class T>
类
#include<iostream>
using namespace std;
template<class NameType,class AgeType>
class Person{
public:
Person(NameType name,AgeType age){
this->m_Name=name;
this->m_Age=age;
}
void showPerson(){
cout<<this->m_Name<<"\t"<<this->m_Age<<endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01(){
Person<string,int> p1("tang",18);
p1.showPerson();
}
int main(void){
test01();
system("pause");
return 0;
}
类模板和函数模板的区别:
类模板没有自动类型推导的使用方式;
类模板在模板参数列表中可以有默认参数。
template<class NameType,class AgeType=int>
class Person{
public:
Person(NameType name,AgeType age){
this->m_Name=name;
this->m_Age=age;
}
void showPerson(){
cout<<this->m_Name<<"\t"<<this->m_Age<<endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01(){
Person<string>p1("tang",18);
p1.showPerson();
}
类模板中成员函数创建时机:
与普通成员函数创建时机有区别:
- 普通—-一开始就可以创建
- 类模板—-调用时才创建
类模板对象做函数参数
传入方式三种:指定类型传入,参数模板化,整个类模板化
#include<iostream>
using namespace std;
template<class NameType,class AgeType>
class Person{
public:
Person(NameType name,AgeType age){
this->m_Name=name;
this->m_Age=age;
}
void showPerson(){
cout<<this->m_Name<<"\t"<<this->m_Age<<endl;
}
NameType m_Name;
AgeType m_Age;
};
//1.指定传入内容,最常用
void printPerson1(Person<string,int> &p){
p.showPerson();
}
//2.参数模板化
template<class NameType,class AgeType>
void printPerson2(Person<NameType,AgeType> &p){
p.showPerson();
cout<<"NameType :"<<typeid(NameType).name()<<endl;
cout<<"AgeType :"<<typeid(AgeType).name()<<endl;
}
//3.整个类模板化
template<class T>
void printPerson3(T &p){
p.showPerson();
cout<<"T :"<<typeid(T).name()<<endl;
}
void test01(){
Person<string,int> p1("tang",48);
//p1.showPerson();
//printPerson1(p1);
//printPerson2(p1);
printPerson3(p1);
}
int main(void){
test01();
system("pause");
return 0;
}
类模板与继承
父类是类模板时,子类在声明时要制定出父类T的类型,否则无法继承,编译器无法给子类分配内存
如果想要灵活指定出父类中T的类型,子类也需要变为类模板。
#include<iostream>
using namespace std;
template<class T>
class Base{
T m;
};
//class Son:public Base{ //错误,必须要知道父类T类型
// class Son:public Base<int>{
// };
//子类也变为类模板
template<class T1,class T2>
class Son:public Base<T2>{
public:
Son(){
cout<<"T1 :"<<typeid(T1).name()<<endl;
cout<<"T2 :"<<typeid(T2).name()<<endl;
}
T1 t1;
};
void test01(){
Son<int,char>s1;
}
int main(void){
test01();
system("pause");
return 0;
}
类模板成员函数的类外实现
#include<iostream>
#include<string>
using namespace std;
template<class NameType,class AgeType>
class Person{
public:
//1.类内实现
// Person(NameType name,AgeType age){
// this->m_Name=name;
// this->m_Age=age;
// }
// void showPerson(){
// cout<<this->m_Name<<"\t"<<this->m_Age<<endl;
// }
//2.类外实现
Person(NameType name,AgeType age);
void showPerson();
NameType m_Name;
AgeType m_Age;
};
template<class NameType,class AgeType>
Person<NameType,AgeType>::Person(NameType name,AgeType age){
this->m_Name=name;
this->m_Age=age;
}
template<class NameType,class AgeType>
void Person<NameType,AgeType>::showPerson(){
cout<<this->m_Name<<"\t"<<this->m_Age<<endl;
}
void test01(){
Person<string,int> p("tang",18);
p.showPerson();
}
int main(void){
test01();
system("pause");
return 0;
}
类模板分文件编写
- 产生问题:类模板成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:1. 直接包含cpp源文件 #include “person.cpp”
- 将实现.cpp和声明.h写到同一文件,并更改后缀名为.hpp
见示例 06-ClassTemplate
- 将实现.cpp和声明.h写到同一文件,并更改后缀名为.hpp
类模板与友元
全局函数 类内实现:类内声明;
类外实现:需要让编译器知道全局函数的存在(友元函数写到前边),并加空参数列表
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class Person;
template<class T1,class T2>
void printPerson2(Person<T1,T2> p){
cout<<p.m_Name<<"\t"<<p.m_Age<<endl;
}
template<class T1,class T2>
class Person{
//1. 类内实现
friend void printPerson(Person<T1,T2> p){
cout<<p.m_Name<<"\t"<<p.m_Age<<endl;
}
//2. 类外实现,空参数列表(让函数声明和实现一致,不加的话,函数实现为函数模板,声明为函数,不一致),在class实现前边 实现 友元函数 ,让编译器知道它的存在。
friend void printPerson2<>(Person<T1,T2> p);
public:
Person(T1 name,T2 age){
this->m_Name=name;
this->m_Age=age;
}
private:
T1 m_Name;
T2 m_Age;
};
void test01(){
Person<string,int> p("tang",28);
printPerson2(p);
}
int main(){
test01();
system("pause");
return 0;
}
案例-实现一个通用的数组类
- STL
2.1 基本概念:
- 为了提升复用性——>C++的面向对象和泛型编程思想
- STL(standard template library标准模板库)
- 从广义上分为 容器container 、算法algorithm、迭代器 iterator,容器和算法之间用迭代器来无缝连接
- STL几乎所有的代码都采用模板类或模板函数。
2.2 六大组件:
分别是 容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
- 容器:各种数据结构,如vector,list, deque,set,map等,存放数据
- 算法:如sort,find,copy,for_each等;
- 迭代器:容器和算法之间的胶合剂。
- 仿函数: 类似函数,可以作为算法的某种策略,其实就是运算符()的重载。
- 适配器:修饰容器、仿函数、迭代器接口的东西。
- 空间配置器:负责空间的配置与管理。
容器、算法、迭代器
- 容器:分为序列式容器 和关联式容器(前者强调值的排序,每个元素有固定位置,后者为二叉树结构,元素之间没有严格的物理上的顺序关系)
- 算法:有限的步骤,解决逻辑或数学上的问题。分为 质变算法和非质变算法。(前者会更改区间元素内容,如copy,后者不会更改,如查找)
- 迭代器:算法通过迭代器访问容器中的元素,而无需暴露容器内部表示方式。每个容器都有专属的迭代器,类似于指针。
迭代器种类:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。(双向和随机访问迭代器较常用)
2.3 初识
vector 理解为数组。遍历算法 for_each.
//存放内置数据类型
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void printNum(int num){
cout<<num<<endl;
}
void test01(){
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
/**
//第一种遍历
//起始迭代器,指向vector第一个元素位置
vector<int>::iterator itBegin=v.begin();
//结束迭代器,指向vector最后一个元素的下一个位置
vector<int>::iterator itEnd=v.end();
while(itBegin!=itEnd){
cout<<*itBegin<<endl;
itBegin++;
}
//第二种遍历
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<endl;
}
**/
//第三种遍历, 利用算法 for_each,包含头文件 algorithm
for_each(v.begin(),v.end(),printNum);
}
int main(){
test01();
system("pause");
return 0;
}
//存放自定义数据类型,数据
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Person{
public:
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
string m_Name;
int m_Age;
};
void test01(){
vector<Person> v;
Person p1("t1",1);Person p2("t1",1);
Person p3("t3",3);Person p4("t3",3);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
//遍历数
for(vector<Person>::iterator it=v.begin();it!=v.end();it++){
//cout<<(*it).m_Name<<"\t"<<(*it).m_Age<<endl;
cout<<it->m_Name<<"\t"<<it->m_Age<<endl;
}
}
int main(){
test01();
system("pause");
return 0;
}
//自定义数据类型,指针
void test01(){
vector<Person*> v;
Person p1("t1",1);Person p2("t1",1);
Person p3("t3",3);Person p4("t3",3);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
//遍历数
for(vector<Person*>::iterator it=v.begin();it!=v.end();it++){
cout<<(*it)->m_Name<<"\t"<<(*it)->m_Age<<endl;
}
}
容器嵌套容器——二维数组
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void test01(){
vector<vector<int>> v;
vector<int> v1;
vector<int> v2;
vector<int> v3;
vector<int> v4;
for(int i=0;i<4;i++){
v1.push_back(i+1);
v2.push_back(i+2);
v3.push_back(i+3);
v4.push_back(i+4);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
for(vector<vector<int>>::iterator it=v.begin();it!=v.end();it++){
for(vector<int>::iterator vit=(*it).begin();vit!=(*it).end();vit++){
cout<<*vit<<"\t";
}
cout<<endl;
}
}
int main(){
test01();
system("pause");
return 0;
}
2.4 容器 string
本质上是一个类
string与 char 的区别:后者是一个指针,前者是一个类,内部封装了char ,管理这个字符串,是一个 char型的容器,内部封装了很多成员方法,如find,copy,delete,replace,insert等,string 管理char所分配的内存。
string构造函数原型
string(); string(const char* s); string(const string& str); string(int n,char c);
void test01(){
string s1;
const char *s="hello world!";
string s2(s);
cout<<s2<<endl;
string s3(3,'a');
cout<<s3<<endl;
}
赋值函数原型 = assign
string& operator=(const char s); string& operator=(const string& str); string& operator=(char c); string& assign(const char s); string& assign(const char *s,int n); 把s前n和赋值过去 string& assign(const string &str); string& assign(int n, char c);
拼接 += append
string& operator+=(const char s); string& operator+=(const string& str); string& operator+=(char c); string& assign(const char s); string& assign(const char *s,int n); 把前n个字符拼接过去 string& assign(const string &str); string& assign(const string &str,int pos, int n); 字符串从pos开始的n个字符过去
c++ 函数前面和后面 使用const 的作用:
- 前面使用const 表示返回值为const
- 后面加 const表示函数不可以修改class的成员
查找和替换 find (从左往右) rfind (从右往左) replace
函数原型
- int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找
- int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找
- int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置
- int find(const char c, int pos = 0) const; //查找字符c第一次出现位置
- 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最后一次出现位置
- string& replace(int pos, int n, const string& str); //替换从pos开始n个字符为字符串str
- string& replace(int pos, int n,const char* s); //替换从pos开始的n个字符为字符串s
string 比较ascii compare s2.compare(s1)
- int compare(const string &s) const; //与字符串s比较
- int compare(const char *s) const; //与字符串s比较
单个字符存取 []或 at s1[8];s1.at(1); 可以访问或修改。
插入和删除函数原型 insert erase
- string& insert(int pos, const char* s); //插入字符串
- string& insert(int pos, const string& str); //插入字符串
- string& insert(int pos, int n, char c); //在指定位置插入n个字符c
- string& erase(int pos, int n = npos); //删除从Pos开始的n个字符
子串 substring
string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串
2.5 vector容器
和数组非常相似,也称单端数组,不同之处在于数组是静态空间,而vector可以动态扩展。
动态扩展是指 找更大的内存空间,将元数据拷贝到新空间,并释放原空间。
vector的迭代器是支持随机访问的迭代器。
构造函数
vector<T> v;
//采用模板实现类实现,默认构造函数vector(v.begin(), v.end());
//将v[begin(), end())区间中的元素拷贝给本身。vector(n, elem);
//构造函数将n个elem拷贝给本身。vector(const vector &vec);
//拷贝构造函数
void printVector(vector<int> &v){
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
printVector(v1);
vector<int> v2(v1.begin(),v1.end());
printVector(v2);
vector<int> v3(5,20);
printVector(v3);
vector<int> v4(v3);
printVector(v4);
}
赋值
vector& operator=(const vector &vec);
//重载等号操作符assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。assign(n, elem);
//将n个elem拷贝赋值给本身。
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
printVector(v1);
vector<int> v2=v1;
printVector(v2);
vector<int> v3;
v3.assign(v2.begin(),v2.end());
printVector(v3);
vector<int> v4;
v4.assign(3,10);
printVector(v4);
}
容量和大小
empty();
//判断容器是否为空capacity();
//容器的容量size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。resize(int num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
printVector(v1);
if(v1.empty()){
cout<<"v1为空"<<endl;
}else{
cout<<"v1不为空"<<endl;
cout<<"capacity:"<<v1.capacity()<<endl;
cout<<"size:"<<v1.size()<<endl;
}
printVector(v1);
v1.resize(6);
printVector(v1);
v1.resize(8,10);
printVector(v1);
v1.resize(5);
printVector(v1);
}
插入和删除
push_back(ele);
//尾部插入元素elepop_back();
//删除最后一个元素insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele);
//迭代器指向位置pos插入count个元素eleerase(const_iterator pos);
//删除迭代器指向的元素erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
printVector(v1);
v1.pop_back();
printVector(v1);
v1.insert(v1.begin(),520);
printVector(v1);
v1.insert(v1.begin(),2,520);
printVector(v1);
v1.erase(v1.begin());
printVector(v1);
v1.erase(v1.begin(),v1.begin()+3);
printVector(v1);
v1.clear();
printVector(v1);
}
数据存取:迭代器或下列的方法
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
for(int i=0;i<5;i++){
cout<<v1[i]<<"\t";
}
cout<<endl;
for(int i=0;i<5;i++){
cout<<v1.at(i)<<"\t";
}
cout<<endl;
cout<<"first element:"<<v1.front()<<endl;
cout<<"last element"<<v1.back()<<endl;
}
互换容器:实现两个容器内元素的互换。
- swap(vec); // 将vec与本身的元素互换
巧用可以实现内存的收缩—原理:
vector
我们先通过vector
在c++11及以后版本中,也可直接使用成员函数 shrink_to_fit
void test01(){
vector<int> v1;
for(int i=0;i<5;i++){
v1.push_back(i);
}
printVector(v1);
vector<int> v2;
for(int i=0;i<5;i++){
v2.push_back(5-i);
}
printVector(v2);
swap(v1,v2);
printVector(v1);
printVector(v2);
}
//实际用途:巧用swap可以收缩内存空间
void test02(){
vector<int> v;
for(int i=0;i<10000;i++){
v.push_back(i);
}
cout<<"v capacity:"<<v.capacity()<<endl;
cout<<"v size:"<<v.size()<<endl;
v.resize(10);
cout<<"resize capacity:"<<v.capacity()<<endl;
cout<<"resize size:"<<v.size()<<endl;
//(v)是一个匿名对象,先用拷贝构造函数造出一个size=v.size()的容器
//然后交换,匿名对象就指向了原来的大空间,swap执行完后 匿名对象被系统回收。
vector<int> (v).swap(v);
cout<<"shrink capacity:"<<v.capacity()<<endl;
cout<<"shrink size:"<<v.size()<<endl;
}
预留空间:可以减少vector在动态扩展容量时的扩展次数。
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
void test01(){
vector<int> v1;
//预留空间
v1.reserve(100000);
int num=0;//统计开辟空间次数
int *p=NULL;
for(int i=0;i<100000;i++){
v1.push_back(i);
if(p!=&v1[0]){
p=&v1[0];
num++;
}
}
cout<<"num:"<<num<<endl;
}
2.6 deque容器
双端数组,可以对头端进行插入删除操作
deque与vector区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低,需要依次后移
- deque相对而言,对头部的插入删除速度回比vector快
- vector访问元素时的速度会比deque快,这和两者内部实现有关。
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间,访问元素时可能要通过多个节点去找到该元素,速度比vector慢,vector是一个连续的线性表。
- deque容器的迭代器也是支持随机访问的
deque构造函数:
deque<T>
deqT; //默认构造形式deque(beg, end);
//构造函数将[beg, end)区间中的元素拷贝给本身。deque(n, elem);
//构造函数将n个elem拷贝给本身。deque(const deque &deq);
//拷贝构造函数
void printDeque(const deque<int> &d){
for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
deque<int> d;
for(int i=0;i<5;i++){
d.push_back(i);
}
printDeque(d);
deque<int> d2(d);
printDeque(d2);
deque<int> d3(3,100);
printDeque(d3);
deque<int> d4(d.begin(),d.end());
printDeque(d4);
}
deque赋值
deque& operator=(const deque &deq);
//重载等号操作符assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。assign(n, elem);
//将n个elem拷贝赋值给本身
deque大小操作,
- deque.empty(); //判断容器是否为空
- deque.size(); //返回容器中元素的个数
- deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。 //如果容器变短,则末尾超出容器长度的元素被删除。
- deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
- 没有 容量 概念
deque插入和删除
- 两端插入操作:
- push_back(elem); //在容器尾部添加一个数据
- push_front(elem); //在容器头部插入一个数据
- pop_back(); //删除容器最后一个数据
- pop_front(); //删除容器第一个数据
指定位置操作: 提供的位置一定要是迭代器
- insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
- insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
- insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
- clear(); //清空容器的所有数据
- erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
- erase(pos); //删除pos位置的数据,返回下一个数据的位置。
数据存取
at(int idx);
//返回索引idx所指的数据operator[];
//返回索引idx所指的数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
排序算法
- sort(iterator beg, iterator end) //对beg和end区间内元素进行排序
对于支持随机访问的迭代器的容器,都可以用sort直接排序,如vector、deque。
void test01(){
deque<int> d;
d.push_back(10);
d.push_back(200);
d.push_back(30);
printDeque(d);
//默认升序
sort(d.begin(),d.end());
printDeque(d);
}
案例 评委打分
有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。
实现步骤:
- 创建五名选手,放到vector中
- 遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分存到deque容器中
- sort算法对deque容器中分数排序,去除最高和最低分
- deque容器遍历一遍,累加总分
- 获取平均分
2.7 stack容器
stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为,不提供迭代器
栈中进入数据称为 —- 入栈push
栈中弹出数据称为 —- 出栈pop
构造函数:
stack<T> stk;
//stack采用模板类实现, stack对象的默认构造形式stack(const stack &stk);
//拷贝构造函数赋值操作:
stack& operator=(const stack &stk);
//重载等号操作符数据存取:
push(elem);
//向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素大小操作:
empty();
//判断堆栈是否为空size();
//返回栈的大小
#include<iostream>
#include<stack>
using namespace std;
void test01(){
stack<int> s;
s.push(10);
s.push(20);
s.push(30);
cout<<"s size:"<<s.size()<<endl;
while(!s.empty()){
cout<<s.top()<<endl;
s.pop();
}
cout<<"s size:"<<s.size()<<endl;
}
int main(){
test01();
system("pause");
return 0;
}
2.8 queue容器
Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口.
队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为,不提供迭代器
队列中进数据称为 —- 入队push
队列中出数据称为 —- 出队pop
构造函数:
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);
//拷贝构造函数赋值操作:
queue& operator=(const queue &que);
//重载等号操作符数据存取:
push(elem);
//往队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一个元素front();
//返回第一个元素大小操作:
empty();
//判断堆栈是否为空size();
//返回栈的大小
void test01(){
queue<int> s;
s.push(10);
s.push(20);
s.push(30);
cout<<"s size:"<<s.size()<<endl;
while(!s.empty()){
cout<<"front :"<<s.front()<<" back :"<<s.back()<<endl;;
s.pop();
}
cout<<"s size:"<<s.size()<<endl;
}
2.9 list链表
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的.
链表的组成:链表由一系列结点组成,
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器.
list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
- 链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
构造函数:
list<T> lst;
//list采用采用模板类实现,对象的默认构造形式:list(beg,end);
//构造函数将[beg, end)区间中的元素拷贝给本身。list(n,elem);
//构造函数将n个elem拷贝给本身。list(const list &lst);
//拷贝构造函数。
#include<iostream>
#include<list>
using namespace std;
void printList(const list<int> &l){
for(list<int>::const_iterator it=l.begin();it!=l.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
printList(l1);
list<int> l2(l1);
printList(l2);
list<int> l3(l2.begin(),l2.end());
printList(l3);
list<int> l4(5,20);
printList(l4);
}
int main(){
test01();
system("pause");
return 0;
}
赋值和交换
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。assign(n, elem);
//将n个elem拷贝赋值给本身。list& operator=(const list &lst);
//重载等号操作符swap(lst);
//将lst与本身的元素互换。
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
printList(l1);
list<int> l2;
l2=l1;
printList(l2);
list<int> l3;
l3.assign(l2.begin(),l2.end());
printList(l3);
list<int> l4;
l4.assign(5,20);
printList(l4);
cout<<"-------------swap---------------"<<endl;
cout<<"before:"<<endl;
printList(l1);
printList(l4);
cout<<"after:"<<endl;
l1.swap(l4);
printList(l1);
printList(l4);
}
list容量大小
size();
//返回容器中元素的个数empty();
//判断容器是否为空resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
printList(l1);
if(l1.empty()){
cout<<"l1 is empty"<<endl;
}else{
cout<<"l1 is not empty"<<endl;
cout<<"l1 size :"<<l1.size()<<endl;
}
l1.resize(10);
printList(l1);
l1.resize(2);
printList(l1);
l1.resize(4,10);
printList(l1);
}
list插入和删除
- push_back(elem);//在容器尾部加入一个元素
- pop_back();//删除容器中最后一个元素
- push_front(elem);//在容器开头插入一个元素
- pop_front();//从容器开头移除第一个元素
- insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
- insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
- insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
- clear();//移除容器的所有数据
- erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
- erase(pos);//删除pos位置的数据,返回下一个数据的位置。
- remove(elem);//删除容器中所有与elem值匹配的元素。
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_front(30);
l1.push_front(40);
printList(l1);
l1.pop_back();
printList(l1);
l1.pop_front();
printList(l1);
list<int>::iterator it=l1.begin();
l1.insert(++it,10000);
printList(l1);
it=l1.begin();
l1.erase(++it);
printList(l1);
l1.push_back(10000);
l1.push_back(10000);
printList(l1);
l1.remove(10000);
printList(l1);
l1.clear();
printList(l1);
}
list数据存取
front();
//返回第一个元素。back();
//返回最后一个元素。
list容器中不可以通过[]或者at方式访问数据,可以用++、— 来实现双向移动
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_front(30);
l1.push_front(40);
printList(l1);
cout<<"first element:"<<l1.front()<<endl;
cout<<"last element:"<<l1.back()<<endl;
}
list反转和排序
reverse();
//反转链表sort();
//链表排序
非随机访问迭代器 不可以用算法中的sort算法来排序,其内部提供了排序接口sort(),这是一个成员函数。
bool myCompare(int val1,int val2){
return val1>val2;
}
void test01(){
list<int> l1;
l1.push_back(10);
l1.push_back(200);
l1.push_front(300);
l1.push_front(40);
printList(l1);
l1.reverse();
printList(l1);
//默认升序
l1.sort();
printList(l1);
//参数为函数名,实现降序
l1.sort(myCompare);
printList(l1);
}
案例:
将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高
排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序。
2.10 set/multiset容器
set :所有元素都会在插入时自动被排序
set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
构造
set<T> st;
//默认构造函数:set(const set &st);
//拷贝构造函数
#include<iostream>
#include<set>
using namespace std;
void printSet(const set<int> &s){
for(set<int>::iterator it=s.begin();it!=s.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
set<int> s;
s.insert(10);
s.insert(40);
s.insert(30);
printSet(s);
set<int> s1(s);
printSet(s1);
}
int main(){
test01();
system("pause");
return 0;
}
赋值
- set& operator=(const set &st); //重载等号操作符
大小和交换
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换两个集合容器
void test01(){
set<int> s;
s.insert(10);
s.insert(40);
s.insert(30);
printSet(s);
if(!s.empty()){
cout<<"The set is not empty,its size:"<<s.size()<<endl;
}else{
cout<<"The set is empty."<<endl;
}
set<int> s2;
s2.insert(100);
s2.insert(300);
s2.insert(200);
s2.insert(400);
cout<<"Before swap:"<<endl;
printSet(s);
printSet(s2);
cout<<"After swap:"<<endl;
s.swap(s2);
printSet(s);
printSet(s2);
}
插入和删除
insert(elem);
//在容器中插入元素。clear();
//清除所有元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。erase(elem);
//删除容器中值为elem的元素。
void test01(){
set<int> s;
s.insert(10);
s.insert(40);
s.insert(30);
s.insert(30);
s.insert(60);
s.insert(300);
printSet(s);
set<int>::iterator it=s.begin();
s.erase(++it);
printSet(s);
s.erase(300);
printSet(s);
s.erase(s.begin(),s.end());
printSet(s);
s.insert(10);
s.insert(40);
printSet(s);
s.clear();
printSet(s);
}
查找和统计
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();count(key);
//统计key的元素个数, 返回0或1
void test01(){
set<int> s;
s.insert(10);
s.insert(40);
s.insert(30);
s.insert(30);
s.insert(60);
s.insert(300);
printSet(s);
set<int>::iterator pos=s.find(40);
if(pos!=s.end()){
cout<<"找到了该元素,位置在:"<<*pos<<endl;
}else{
cout<<"未找到该元素"<<endl;
}
int num=s.count(30);
cout<<"The number of 30 :"<<num<<endl;
}
set与multiset的区别:
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
void test01(){ set<int> s; //insert后返回一个pair,pair的第一个值用first来取--->迭代器,第二个用second---->插入成功与否 pair<set<int>::iterator,bool> ret=s.insert(20); if(ret.second){ cout<<"Successful!"<<endl; }else{ cout<<"Fail!"<<endl; } ret=s.insert(20); if(ret.second){ cout<<"Successful!"<<endl; }else{ cout<<"Fail!"<<endl; } multiset<int> ms; ms.insert(20); ms.insert(20); for(multiset<int>::iterator it=ms.begin();it!=ms.end();it++){ cout<<*it<<"\t"; } cout<<endl; }
pair对组
成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 );
```cppinclude
include
using namespace std;
void test01(){
pair
pair<string,int> p1=make_pair("zhou",22);
cout<<p1.first<<"\t"<<p1.second<<endl;
}
int main(){
test01();
system(“pause”);
return 0;
}
set排序
- 默认排序规则为从小到大
- 利用仿函数,可以改变排序规则。 新建数据类型 class ,重写()
```cpp
//存放内置数据类型
class MyCompare{
public:
bool operator()(int val1,int val2){
return val1>val2;
}
};
void test01(){
set<int> s;
s.insert(10);
s.insert(40);
s.insert(30);
s.insert(60);
s.insert(300);
for(set<int>::iterator it=s.begin();it!=s.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
set<int,MyCompare> s2;
s2.insert(10);
s2.insert(40);
s2.insert(30);
s2.insert(60);
s2.insert(300);
for(set<int>::iterator it=s2.begin();it!=s2.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
//自定义数据类型
#include<iostream>
#include<set>
using namespace std;
class Person{
public:
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
string m_Name;
int m_Age;
};
//仿函数 自定义数据类型 排序规则
class comparePerson{
public:
bool operator()(const Person &p1,const Person &p2){
return p1.m_Age>p2.m_Age;
}
};
void test01(){
set<Person,comparePerson> s;
Person p1("刘备", 23);
Person p2("关羽", 27);
Person p3("张飞", 25);
Person p4("赵云", 21);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for(set<Person,comparePerson>::iterator it=s.begin();it!=s.end();it++){
cout<<it->m_Name<<"\t"<<it->m_Age<<endl;
}
}
int main(){
test01();
system("pause");
return 0;
}
2.11 map/multimap容器
简介:
- map中所有元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素都会根据元素的键值自动排序
本质:
- map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
- 可以根据key值快速找到value值
map和multimap区别:
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
set构造函数
map<T1, T2> mp;
//map默认构造函数:map(const map &mp);
//拷贝构造函数
赋值
map& operator=(const map &mp); //重载等号操作符
#include<iostream>
#include<map>
using namespace std;
void printMap(map<int,int> &m){
for(map<int,int>::iterator it=m.begin();it!=m.end();it++){
cout<<"key = "<<(*it).first<<",value = "<<it->second<<endl;
}
cout<<endl;
}
void test01(){
map<int,int> m;
//第1种插入方法
m.insert(make_pair(1,10));
//第2种插入方法
m.insert(pair<int,int>(2,20));
//第3种插入方法
m.insert(map<int,int>::value_type(3,40));
//第4种插入方法,类似于数组
m[4]=50;
printMap(m);
map<int,int> m2(m);
printMap(m2);
map<int,int> m3=m2;
printMap(m3);
}
int main(){
test01();
system("pause");
return 0;
}
大小和交换
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换两个集合容器
void test01(){
map<int,int> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(pair<int,int>(3,40));
printMap(m);
if(!m.empty()){
cout<<"The map is not empty,and its size :"<<m.size()<<endl;
}else{
cout<<"The map is empty."<<endl;
}
cout<<"-------Before swap---------"<<endl;
map<int,int> m1;
m1.insert(pair<int,int>(4,40));
m1.insert(pair<int,int>(5,50));
m1.insert(pair<int,int>(6,60));
printMap(m);
printMap(m1);
cout<<"-------After swap---------"<<endl;
m.swap(m1);
printMap(m);
printMap(m1);
}
插入和删除
insert(elem);
//在容器中插入元素。clear();
//清除所有元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。erase(key);
//删除容器中值为key的元素。
void test01(){
map<int,int> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(pair<int,int>(3,40));
printMap(m);
m.erase(m.begin());
printMap(m);
m.erase(m.begin(),m.end());
printMap(m);
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(pair<int,int>(3,40));
printMap(m);
m.erase(1);
printMap(m);
}
查找和统计
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();count(key);
//统计key的元素个数
void test01(){
map<int,int> m;
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(2,20));
m.insert(pair<int,int>(3,40));
printMap(m);
map<int,int>::iterator pos=m.find(1);
if(pos!=m.end()){
cout<<"找到了key为1的元素,值为"<<(*pos).second<<endl;
}else{
cout<<"没有找到该元素"<<endl;
}
int num=m.count(3);
cout<<"num="<<num<<endl;
}
排序
- map容器默认排序规则为 按照key值进行 从小到大排序,利用仿函数,可以改变排序规则
- 对于自定义数据类型,map必须要指定排序规则,同set容器
```cpp
//内置数据类型
class myCompare{
public:
bool operator()(int val1,int val2){
} };return val1>val2;
void printMap(map
void test01(){
map
```cpp
//自定义数据类型
#include<iostream>
#include<map>
using namespace std;
class Person{
public:
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
string m_Name;
int m_Age;
};
class ComparePerson{
public:
bool operator()(int val1,int val2){
return val1>val2;
}
};
void printMap(map<int,Person,ComparePerson> &m){
for(map<int,Person,ComparePerson>::iterator it=m.begin();it!=m.end();it++){
cout<<"key = "<<(*it).first<<",value = "<<it->second.m_Name<<","<<it->second.m_Age<<endl;
}
}
void test01(){
map<int,Person,ComparePerson> m;
Person p1("刘备", 23);
Person p2("关羽", 27);
Person p3("张飞", 25);
Person p4("赵云", 21);
m.insert(make_pair(1,p1));
m.insert(make_pair(2,p2));
m.insert(make_pair(3,p3));
m.insert(make_pair(4,p4));
printMap(m);
}
int main(){
test01();
system("pause");
return 0;
}
案例-员工分组
- 公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
- 员工信息有: 姓名 工资组成;部门分为:策划、美术、研发
- 随机给10名员工分配部门和工资
- 通过multimap进行信息的插入 key(部门编号) value(员工)
- 分部门显示员工信息
- STL-函数对象
3.1 函数对象
概念:
- 重载函数调用操作符的类,其对象常称为函数对象
- 函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:
函数对象(仿函数)是一个类,不是一个函数
特点:
- 函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
```cpp
include
using namespace std;
class myAdd{ public: int operator()(int val1,int val2){ return val1+val2; } };
class myPrint{ public: myPrint(){ this->count=0; } void operator()(string text){ cout<<text<<endl; count++; } int count; //记录状态 };
class doPrint{ public: void operator()(myPrint &mp,string text){ mp(text); } };
void test01(){ //1. 函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值 myAdd myadd; cout<<myadd(1,2)<<endl;;
//2. 函数对象可以有自己的状态
myPrint myprint;
myprint("Hello world!");
myprint("Hello world!");
myprint("Hello world!");
myprint("Hello world!");
cout<<"myPrint调用了"<<myprint.count<<"次\n";
//3.函数对象可以作为参数传递
doPrint doprint;
doprint(myprint,"Hello");
}
int main(){
test01();
system(“pause”);
return 0;
}
3.2 谓词<br />**概念:**
- 返回bool类型的仿函数称为**谓词**
- 如果operator()接受一个参数,那么叫做一元谓词
- 如果operator()接受两个参数,那么叫做二元谓词
使用函数对象改变算法策略,排序从大到小
```cpp
//一元谓词
class GreaterFive{
public:
bool operator()(int val){
return val>5;
}
};
void printVector(vector<int> &v){
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
vector<int> v;
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
printVector(v);
//输出大于5的数
vector<int>::iterator pos=find_if(v.begin(),v.end(),GreaterFive());
if(pos!=v.end()){
cout<<"找到了"<<*pos<<endl;
}else{
cout<<"没找到"<<endl;
}
}
//二元谓词
class myCompare{
public:
bool operator()(int val1,int val2){
return val1>val2;
}
};
void printVector(vector<int> &v){
for(vector<int>::iterator it=v.begin();it!=v.end();it++){
cout<<*it<<"\t";
}
cout<<endl;
}
void test01(){
vector<int> v;
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
v.push_back(7);
printVector(v);
sort(v.begin(),v.end(),myCompare());
printVector(v);
}
4.3 内建函数对象
STL内建了一些函数对象
分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:
- 这些仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,需要引入头文件
#include<functional>
算术仿函数
- 实现四则运算
template<class T> T plus<T>
//加法仿函数template<class T> T minus<T>
//减法仿函数template<class T> T multiplies<T>
//乘法仿函数template<class T> T divides<T>
//除法仿函数template<class T> T modulus<T>
//取模仿函数,求余template<class T> T negate<T>
//取反仿函数
其中negate是一元运算,其他都是二元运算
void test01(){
//取反 输出
negate<int> n;
cout<<n(30)<<endl;
//minus 输出
plus<int> p;
cout<<p(2,3)<<endl;
//modus 输出
modulus<int> m;
cout<<m(6,4)<<endl;
}
关系仿函数
- 实现关系对比
template<class T> bool greater<T>
//大于 较常用template<class T> bool greater_equal<T>
//大于等于template<class T> bool less<T>
//小于template<class T> bool less_equal<T>
//小于等于
void test01(){
vector<int> v;
v.push_back(7);
v.push_back(8);
v.push_back(5);
v.push_back(6);
v.push_back(3);
printVector(v);
sort(v.begin(),v.end());
printVector(v);
sort(v.begin(),v.end(),greater<int>());
printVector(v);
}
逻辑仿函数
- 实现逻辑运算
template<class T> bool logical_and<T>
//逻辑与template<class T> bool logical_or<T>
//逻辑或template<class T> bool logical_not<T>
//逻辑非
void test01(){
vector<bool> v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(true);
v.push_back(false);
printVector(v);
//逻辑非 将v容器搬运到v2中,并执行逻辑非运算
vector<bool> v2;
//v2容器大小
v2.resize(v.size());
transform(v.begin(),v.end(),v2.begin(),logical_not<bool>());
printVector(v2);
}
- 常用算法
5.1 遍历算法
for_each
//遍历容器transform
//搬运容器到另一个容器中- for_each(iterator beg, iterator end, _func);
// 遍历算法 遍历容器元素 // beg 开始迭代器 // end 结束迭代器 // _func 函数或者函数对象
transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器 //end1 源容器结束迭代器 //beg2 目标容器开始迭代器 //_func 函数或者函数对象
void print01(int val){
cout<<val<<" ";
}
class print02{
public:
void operator()(int val){
cout<<val<<" ";
}
};
void test01(){
vector<int> v;
v.push_back(7);
v.push_back(8);
v.push_back(5);
v.push_back(6);
v.push_back(3);
//普通函数
for_each(v.begin(),v.end(),print01);
cout<<endl;
//函数对象
for_each(v.begin(),v.end(),print02());
}
class myTransform{
public:
int operator()(int val){
return val+10;
}
};
class myPrint{
public:
void operator()(int val){
cout<<val<<" ";
}
};
void test01(){
vector<int> v;
v.push_back(7);
v.push_back(8);
v.push_back(5);
v.push_back(6);
v.push_back(3);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
vector<int> v2;
v2.resize(v.size());
transform(v.begin(),v.end(),v2.begin(),myTransform());
for_each(v2.begin(),v2.end(),myPrint());
}
5.2 查找算法
find
//查找元素find_if
//按条件查找元素adjacent_find
//查找相邻重复元素binary_search
//二分查找法count
//统计元素个数count_if
//按条件统计元素个数
find———内置数据类型和自定义数据类型
查找指定元素,找到返回指定元素的迭代器,找不到返回结束迭代器end()
- find(iterator beg, iterator end, value);// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置// beg 开始迭代器// end 结束迭代器// value 查找的元素
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class myPrint{
public:
void operator()(int val){
cout<<val<<" ";
}
};
class Person{
public:
Person(string name,int age){
this->m_Name=name;
this->m_Age=age;
}
bool operator==(const Person &p){
if(this->m_Age==p.m_Age&&this->m_Name==p.m_Name){
return true;
}else{
return false;
}
}
string m_Name;
int m_Age;
};
void test01(){
//find 内置数据类型
vector<int> v;
v.push_back(7);
v.push_back(8);
v.push_back(5);
v.push_back(6);
v.push_back(3);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
vector<int>::iterator it=find(v.begin(),v.end(),3);
if(it!=v.end()){
cout<<"找到了\n";
}else{
cout<<"没找到\n";
}
//自定义数据类型
vector<Person> vp;
Person p1("aaa",10);
Person p2("bbb",20);
Person p3("ccc",30);
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vector<Person>::iterator pit=find(vp.begin(),vp.end(),p3);
if(pit!=vp.end()){
cout<<"找到了\n";
}else{
cout<<"没找到\n";
}
}
int main(){
test01();
system("pause");
return 0;
}
find_if
- 按条件查找元素
- find_if(iterator beg, iterator end, _Pred);// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置// beg 开始迭代器// end 结束迭代器// _Pred 函数或者谓词(返回bool类型的仿函数)
class GreaterFive{
public:
bool operator()(int val){
return val>5;
}
};
class AgeGreater20{
public:
bool operator()(Person &p){
return p.m_Age>20;
}
};
void test01(){
vector<int> v;
v.push_back(7);
v.push_back(8);
v.push_back(5);
v.push_back(6);
v.push_back(3);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
vector<int>::iterator it=find_if(v.begin(),v.end(),GreaterFive());
if(it!=v.end()){
cout<<"找到了\t"<<*it<<endl;
}else{
cout<<"没找到\n";
}
//自定义数据类型
vector<Person> vp;
Person p1("aaa",10);
Person p2("bbb",20);
Person p3("ccc",30);
vp.push_back(p1);
vp.push_back(p2);
vp.push_back(p3);
vector<Person>::iterator pit=find_if(vp.begin(),vp.end(),AgeGreater20());
if(pit!=vp.end()){
cout<<"找到了\t"<<pit->m_Name<<","<<pit->m_Age<<endl;
}else{
cout<<"没找到\n";
}
}
adjacent_find
- 查找相邻重复元素
adjacent_find(iterator beg, iterator end); // 查找相邻重复元素,返回相邻元素的第一个位置的迭代器 // beg 开始迭代器 // end 结束迭代器
void test01(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(5);
v.push_back(2);
v.push_back(4);
v.push_back(4);
v.push_back(3);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
vector<int>::iterator it=adjacent_find(v.begin(),v.end());
if(it!=v.end()){
cout<<"找到了\t"<<*it<<endl;
}else{
cout<<"没找到\n";
}
}
binary_search
- 查找指定元素是否存在
- bool binary_search(iterator beg, iterator end, value);// 查找指定的元素,查到 返回true 否则false// 注意: 在无序序列中不可用// beg 开始迭代器// end 结束迭代器// value 查找的元素
void test01(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(5);
v.push_back(2);
v.push_back(4);
v.push_back(4);
v.push_back(3);
sort(v.begin(),v.end());
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
bool it=binary_search(v.begin(),v.end(),3);
if(it){
cout<<"找到了\n"<<endl;
}else{
cout<<"没找到\n";
}
}
二分查找法查找效率很高,值得注意的是查找的容器中元素必须的有序序列
count
- 统计元素个数
- count(iterator beg, iterator end, value);// 统计元素出现次数// beg 开始迭代器// end 结束迭代器// value 统计的元素
void test01(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(3);
v.push_back(4);
v.push_back(4);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
int num=count(v.begin(),v.end(),4);
cout<<num<<endl;
}
总结: 统计自定义数据类型时候,需要配合重载 operator==
count_if
- 按条件统计元素个数
count_if(iterator beg, iterator end, _Pred); // 按条件统计元素出现次数 // beg 开始迭代器 // end 结束迭代器 // _Pred 谓词
class GreaterFive{
public:
bool operator()(int val){
return val>5;
}
};
void test01(){
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(3);
v.push_back(7);
v.push_back(6);
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
int num=count_if(v.begin(),v.end(),GreaterFive());
cout<<num<<endl;
}
5.3 排序算法
sort
//对容器内元素进行排序random_shuffle
//洗牌 指定范围内的元素随机调整次序merge
// 容器元素合并,并存储到另一容器中reverse
// 反转指定范围的元素
sort
sort(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置 // beg 开始迭代器 // end 结束迭代器 // _Pred 谓词
random_shuffle
- 比较实用,使用时记得加随机数种子
- 洗牌 指定范围内的元素随机调整次序
random_shuffle(iterator beg, iterator end); // 指定范围内的元素随机调整次序 // beg 开始迭代器 // end 结束迭代器
void test01(){
vector<int> v;
for(int i=0;i<10;i++){
v.push_back(i);
}
for_each(v.begin(),v.end(),myPrint());
cout<<endl;
srand((unsigned int)time(NULL));
random_shuffle(v.begin(),v.end());
for_each(v.begin(),v.end(),myPrint());
}
merge
- 两个容器元素合并,并存储到另一容器中
- merge合并的两个容器必须的有序序列
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); // 容器元素合并,并存储到另一容器中 // 注意: 两个容器必须是有序的 // beg1 容器1开始迭代器// end1 容器1结束迭代器// beg2 容器2开始迭代器// end2 容器2结束迭代器// dest 目标容器开始迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
v2.push_back(i+1);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
vector<int> vTarget;
//vTarget 开辟容量
vTarget.resize(v1.size()+v2.size());
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
for_each(vTarget.begin(),vTarget.end(),myPrint());
cout<<endl;
}
reverse
- 将容器内元素进行反转
reverse(iterator beg, iterator end); // 反转指定范围的元素 // beg 开始迭代器 // end 结束迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
reverse(v1.begin(),v1.end());
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
}
5.4 拷贝和替换
copy
// 容器内指定范围的元素拷贝到另一容器中replace
// 将容器内指定范围的旧元素修改为新元素replace_if
// 容器内指定范围满足条件的元素替换为新元素swap
// 互换两个容器的元素
copy
- 容器内指定范围的元素拷贝到另一容器中
- copy(iterator beg, iterator end, iterator dest);// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置// beg 开始迭代器// end 结束迭代器// dest 目标起始迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
v2.resize(v1.size());
copy(v1.begin(),v1.end(),v2.begin());
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
}
目标容器记得提前开辟空间.
replace
- 将容器内指定范围的旧元素修改为新元素
- replace(iterator beg, iterator end, oldvalue, newvalue);// 将区间内旧元素 替换成 新元素// beg 开始迭代器// end 结束迭代器// oldvalue 旧元素// newvalue 新元素
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
replace(v1.begin(),v1.end(),3,2);
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
replace(v1.begin(),v1.end(),2,2000);
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
}
replace_if
- 将区间内满足条件的元素,替换成指定元素
- replace_if(iterator beg, iterator end, _pred, newvalue);// 按条件替换元素,满足条件的替换成指定元素// beg 开始迭代器// end 结束迭代器// _pred 谓词// newvalue 替换的新元素
class GreaterFive{
public:
bool operator()(int val){
return val>5;
}
};
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
replace_if(v1.begin(),v1.end(),GreaterFive(),2000);
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
swap
- 互换两个容器的元素,交换的容器要同种类型
swap(container c1, container c2);
// 互换两个容器的元素 // c1容器1 // c2容器2
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
v2.push_back(i+100);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
swap(v1,v2);
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
5.5 算术生成算法
- 算术生成算法属于小型算法,使用时包含的头文件为 #include
accumulate
// 计算容器元素累计总和fill
// 向容器中添加元素
accumulate
- 计算区间内 容器元素累计总和
accumulate(iterator beg, iterator end, value);
// 计算容器元素累计总和
// beg 开始迭代器
// end 结束迭代器
// value 起始值
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
int total=accumulate(v1.begin(),v1.end(),0);
cout<<total<<endl;
}
fill
- 向容器中填充指定的元素
fill(iterator beg, iterator end, value);// 向容器中填充元素// beg 开始迭代器// end 结束迭代器// value 填充的值
void test01(){
vector<int> v1;
v1.resize(10);
fill(v1.begin(),v1.end(),10);
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
}
5.6 集合算法
set_intersection
// 求两个容器的交集set_union
// 求两个容器的并集set_difference
// 求两个容器的差集
set_intersection
- 求两个容器的交集
- set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);// 求两个集合的交集// 注意:两个集合必须是有序序列// beg1 容器1开始迭代器// end1 容器1结束迭代器// beg2 容器2开始迭代器// end2 容器2结束迭代器// dest 目标容器开始迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
v2.push_back(i+5);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
vector<int> vTarget;
vTarget.resize(min(v1.size(),v2.size()));
vector<int>::iterator itEnd=set_intersection(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
for_each(vTarget.begin(),itEnd,myPrint());
cout<<endl;
}
注意:
- 求交集的两个集合必须的有序序列
- 目标容器开辟空间需要从两个容器中取小值
- set_intersection返回值既是交集中最后一个元素的位置
set_union
- 求两个集合的并集
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); // 求两个集合的并集 // 注意:两个集合必须是有序序列 // beg1 容器1开始迭代器// end1 容器1结束迭代器// beg2 容器2开始迭代器// end2 容器2结束迭代器// dest 目标容器开始迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
v2.push_back(i+5);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
vector<int> vTarget;
vTarget.resize(v1.size()+v2.size());
vector<int>::iterator itEnd=set_union(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
for_each(vTarget.begin(),itEnd,myPrint());
cout<<endl;
注意:
- 求并集的两个集合必须的有序序列
- 目标容器开辟空间需要两个容器相加
- set_union返回值既是并集中最后一个元素的位置
set_difference
- 求两个集合的差集
- set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);// 求两个集合的差集// 注意:两个集合必须是有序序列// beg1 容器1开始迭代器// end1 容器1结束迭代器// beg2 容器2开始迭代器// end2 容器2结束迭代器// dest 目标容器开始迭代器
void test01(){
vector<int> v1,v2;
for(int i=0;i<10;i++){
v1.push_back(i);
v2.push_back(i+5);
}
for_each(v1.begin(),v1.end(),myPrint());
cout<<endl;
for_each(v2.begin(),v2.end(),myPrint());
cout<<endl;
vector<int> vTarget;
vTarget.resize(max(v1.size(),v2.size()));
//v1 与 v2 的差集
vector<int>::iterator itEnd=set_difference(v1.begin(),v1.end(),v2.begin(),v2.end(),vTarget.begin());
cout<<"v1 与 v2 的差集:";
for_each(vTarget.begin(),itEnd,myPrint());
cout<<endl;
//v2 与 v1 的差集
itEnd=set_difference(v2.begin(),v2.end(),v1.begin(),v1.end(),vTarget.begin());
cout<<"v2 与 v1 的差集:";
for_each(vTarget.begin(),itEnd,myPrint());
cout<<endl;
}
注意:
- 求差集的两个集合必须的有序序列
- 目标容器开辟空间需要从两个容器取较大值
- set_difference返回值既是差集中最后一个元素的位置
综合案例
- 演进比赛流程管理系统
比赛规则
程序功能:
- 机房预约系统