C++基础学习笔记(下)
文章目录
一、模板
(一)函数模板
-
C++的另一种编程思想为泛型编程,主要利用模板技术。
-
C++提供两种模板机制:函数模板和类模板。
1.函数模板语法
函数模板的作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表。
语法:使用关键字:template
template<typename T>
函数声明或定义
-
template:声明创建模板;
-
typename:表明其后面的符号是一种数据类型,可以用class代替;
T:通用的数据类型,名称可以替换,通常为大写字母;
示例:交换两个未知类型的数
//声明一个模板,告诉编译器后面的 T 是一个通用数据类型
template <typename T>
void swapNum(T &a, T &b){
T temp = a;
a = b;
b = temp;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
}
void test(){
int a = 10;
int b = 20;
//第1种:自动类型推导
swapNum(a, b);
//第2种:显式指定类型
swapNum <int>(a, b);
}
2.函数模板注意事项
(1)自动类型推导时,必须推导出一致的数据类型T才可以使用
(2)使用模板时必须确定出T的数据类型才可以使用。若不指定T数据类型则不能调用函数。
3.函数模板示例
封装一个选择排序算法从大到小对不同数据类型的数组进行排序。使用char数组和int数组测试。
template <class T>
void swapNums(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
template <class T>
void printArr(T arr[], int len){
for (int i = 0; i < len; ++i) {
cout << arr[i] << " " << endl;
}
}
template <class T>
void selectSort(T arr[], int len){
for (int i = 0; i < len; ++i) {
int max = i;//认定最大值的下标
for (int j = i+1; j < len; ++j) {
//若认定的最大值比遍历出的数值要小,说明j下标的元素才是最大值
if (arr[max] < arr[j]){
max = j;
}
}
if (max != i){
//交换max和i的元素
swapNums(arr[max], arr[i]);
}
}
}
void sortTest(){
//输入字符串数组
char charArr[] = "agfcvd";
int num = sizeof(charArr) / sizeof(char);
selectSort(charArr, num);
printArr(charArr, num);
//输入int数组
int arr[] = {12,4,1,0,6,3};
int num1 = sizeof(arr) / sizeof(int);
selectSort(arr, num1);
printArr(arr, num1);
}
4.普通函数和函数模板的区别
(1)普通函数调用时可以自动进行类型转换(隐式类型转换),如将字符型变量变为ASCII码的int型数值;
(2)函数模板调用时,如果用自动类型推导,不会发生隐式类型转换,而显式指定类型的方式可以发生隐式类型转换。
5.普通函数和函数模板的调用规则
(1)优先调用普通函数;
(2)可以通过空模板参数列表来强制调用函数模板;
(3)函数模板可以发生重载;
(4)若函数模板可以产生更好的匹配则优先调用函数模板。
void printNum(int a, int b){
cout << "调用函数" << endl;
}
template <class T>
void printNum(T a, T b){
cout << "调用模板函数" << endl;
}
template <class T>
void printNum(T a, T b, T c){
cout << "调用重载模板函数" << endl;
}
int main(){
int a = 1, b = 2, c = 0;
//(1)优先调用普通函数
printNum(a, b);
//(2)通过空模板参数列表强制调用模板函数
printNum<>(a, b);
//(3)调用重载模板函数
printNum(a, b, c);
//(4)虽然普通函数可以隐式类型转换,但匹配模板函数更好
char d = 'd',e = 'e';
printNum(d, e);
}
6.模板的局限性
对于某些特定数据类型需要用具体化方式做特殊实现。示例:比较两个Person类
class Person{
public:
Person(string name, int age){
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
};
template <class T>
bool compare(T &a, T &b){
if(a == b){
return true;
} else{
return false;
}
}
//第1种:可以采用运算符重载
//第2种:利用具体化Person的版本实现
template <> bool compare(Person &a, Person &b){
if(a.m_name == b.m_name && a.m_age == b.m_age){
return true;
} else{
return false;
}
}
void test(){
Person p1("小a", 10);
Person p2("小b", 20);
int result = compare(p1, p2);
if (result == true){
cout << "相等!" << endl;
} else{
cout << "不等!" << endl;
}
}
(二)类模板
1.类模板作用和语法
(1)作用:建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟类型来代表。
(2)语法:
template <typename T>
类
2.类模板和函数模板区别
(1)类模板没有自动类型推导方式;
(2)类模板在模板参数列表中可以有默认参数。
template <class NameType, class AgeType = int>//类模板中,可以指定默认类型参数
class Person1{
public:
Person1(NameType name, AgeType age){
this->mName = name;
this->mAge = age;
}
void showInfo(){
cout << this->mName << endl;
cout << this->mAge << endl;
}
NameType mName;
AgeType mAge;
};
int main(){
//此行错误!不能使用自动类型推导
// Person p0("wzh", 10);
//应该显式指定类型
Person1 <string> p1("wzh", 10);
p1.showInfo();
}
3.类模板中的成员函数创建时机
类模板和普通类中的成员函数创建时机不同:普通类的成员函数一开始可以创建,类模板中的成员函数在调用时才创建。
class Person1{
public:
void showInfo1(){
cout << "show person1" << endl;
}
};
class Person2{
public:
void showInfo2(){
cout << "show person2" << endl;
}
};
template <class T>
class MyClass{
public:
T obj;
//类模板中的成员函数
void func1(){
obj.showInfo1();
}
void func2(){
obj.showInfo2();
}
};
int main(){
MyClass <Person1> m;
m.func1();
// m.func2();此步报错,函数调用时才会创建成员函数
}
4.类模板对象作为函数参数
类模板实例化出的对象向函数传参公有三种传入方式:(主要用第一种)
(1)指定传入的类型:直接显示对象的数据类型;
(2)参数模板化:将对象中的参数变为模板进行传递;
(3)整个类模板化:将这个对象类型模板化进行传递。
template <class T1, class T2>
class Person{
public:
Person(T1 name, T2 age){
this->mName = name;
this->mAge = age;
}
void showInfo(){
cout << "姓名:" << this->mName << "年龄:" << this->mAge << endl;
}
T1 mName;
T2 mAge;
};
//第1种:指定传入的类型
void printPerson1(Person<string, int>&p){
p.showInfo();
}
//第2种:参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p){
p.showInfo();
}
//第3种:整个类模板化
template <class T>
void printPerson3(T &p){
p.showInfo();
}
int main(){
Person<string, int>p("w", 20);
printPerson1(p);
printPerson2(p);
printPerson3(p);
}
5.类模板和继承
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型;若不指定,编译器无法给子类分配内存;若想灵活指定出父类中T的类型,子类也需变为类模板。
template <class T>
class Base{
T m;
};
class Son1:public Base<int>{//1.此处须指定父类的T数据类型
};
//2.想灵活指定父类中的T类型,子类须变为类模板
template <class T1, class T2>
class Son2:public Base<T2>{
public:
Son2(){
cout << "T1类型:" << typeid(T1).name() << endl;//输出 int
cout << "T2类型:" << typeid(T2).name() << endl;//输出 char
}
};
int main(){
Son2<int, char>s2;
}
6.类模板成员函数类外实现
需要加上模板参数列表:
template <class T1, class T2>
class Person{
public:
Person(T1 name, T2 age);//构造函数
void showInfo();//成员函数
T1 mName;
T2 mAge;
};
//构造函数 类外实现
template <class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age){
this->mName = name;
this->mAge = age;
}
//构造函数 类外实现
template <class T1, class T2>
void Person<T1, T2>:: showInfo(){
cout << "名字:" << this->mName << endl;
cout << "年龄:" << this->mAge << endl;
}
int main(){
Person<string, int> p("wzh", 18);
p.showInfo();
}
7.类模板份文件编写
由于类模板中成员函数创建时机是在调用阶段,导致分文件(.cpp .h)编写时会链接不到,因此有两种解决方式:(1)直接包含.cpp源文件(不推荐);(2)将声明和实现写到同一个文件中,后缀改为.hpp(约定的后缀)。
8.类模板和友元
-
全局函数类内实现:直接在类内声明友元即可;
-
全局函数类外实现:需要提前让编译器知道全局函数的存在。
//1.通过全局函数打印信息
template <class T1, class T2>
class Person{
//1.全局函数 类内实现
friend void printInfo(Person<T1, T2>p){
cout << "名字:" << p.mName << endl;
cout << "年龄:" << p.mAge << endl;
}
public:
Person(T1 name, T2 age){
this->mName = name;
this->mAge = age;
}
private:
T1 mName;
T2 mAge;
};
int main(){
//1.类内调用
Person<string, int>p1("tom", 22);
printInfo(p1);
}
//类外实现,需要提前让编译器知道Person类存在
template <class T1,class T2>
class Person;
//通过全局函数打印信息
template <class T1,class T2>
void printInfo(Person<T1, T2>p){
cout << p.mName << endl;
cout << p.mAge << endl;
}
template <class T1, class T2>
class Person{
//2.全局函数 类外实现,要加空模板参数列表
//全局函数类外实现,需要让编译器提前知道这个函数的存在
friend void printInfo<>(Person<T1, T2>p);
public:
Person(T1 name, T2 age){
this->mName = name;
this->mAge = age;
}
private:
T1 mName;
T2 mAge;
};
int main(){
//类外实现
Person<string, int>p("tom", 22);
printInfo(p);
}
9.类模板案例
(1)目的:实现一个通用的数组类:
可以对内置数据类型以及自定义数据类型的数据进行传输;
将数组中的数据存储到堆区;
构造函数中可以传入数组的容量;
提供对应的拷贝构造函数以及operator=防止浅拷贝问题;
提供尾插法和尾删法对数组中的数据进行增加和删除;
可以通过下标的方式访问数组中的元素;
可以获取数组中当前元素个数和数组容量。
(2)思路:
首先,类中有T * pAddr,返回新开辟的T类型数据的指针,维护数组中的数据;有容量mCapa,有mSize。
其次,提供构造函数接收容量;提供拷贝构造函数和operator=实现深拷贝防止浅拷贝;利用下标访问数组中元素的方法等。
(2)实现:
//这是MyArr.hpp文件
template <class T>
class MyArr{
public:
MyArr(int capa){
// cout << "有参构造函数调用" << endl;
this->mCapa = capa;
this->mSize = 0;
this->pAddr = new T[this->mCapa];//开辟堆区空间
}
//拷贝构造
MyArr(const MyArr& arr){
// cout << "拷贝构造函数调用" << endl;
this->mCapa = arr.mCapa;
this->mSize = arr.mSize;
this->pAddr = arr.pAddr;//会产生浅拷贝
//改为以下,深拷贝
this->pAddr = new T[arr.mCapa];
//拷贝arr中的数据
for (int i = 0; i < this->mSize; ++i) {
this->pAddr[i] = arr.pAddr[i];
}
}
//operator=防止浅拷贝问题
MyArr&operator=(const MyArr& arr){
// cout << "operator调用" << endl;
//先判断原来堆区是否有数据,有则释放
if (this->pAddr != NULL){
delete [] this->pAddr;
this->pAddr = NULL;
this->mCapa = 0;
this->mSize = 0;
}
this->mCapa = arr.mCapa;
this->mSize = arr.mSize;
this->pAddr = new T[arr.mCapa];
for (int i = 0; i < mSize; ++i) {
this->pAddr[i] = arr.pAddr[i];
}
return *this;
}
//尾插法
void pushBack(const T &val){
//判断容量是否等于大小
if (this->mCapa == this->mSize){
return;
}
//插到数组中最后一个位置
this->pAddr[this->mSize] = val;
//更新数组的大小
this->mSize ++;
}
//尾删法
void popBack(){
//让用户访问不到最后一个元素,逻辑删除
if (this->mSize == 0){
return;
}
this->mSize --;
}
//因为是自定义的模板类,不能通过数组下标访问元素
//下面实现通过下标访问数组中的元素
//函数调用要作为左值存在,要返回引用
T& operator[](int index){
return this->pAddr[index];
}
//返回数组容量
int getCapa(){
return this->mCapa;
}
//返回数组大小
int getSize(){
return this->mSize;
}
//析构函数
~MyArr(){
// cout << "析构函数调用" << endl;
if(this->pAddr != NULL){
delete [] this->pAddr;
this->pAddr = NULL;
}
}
private:
T * pAddr;
int mCapa;//数组容量
int mSize;//传入数组大小
};
//这是主文件
#include "MyArr.hpp"
//自定义数据类型
class Person{
public:
Person(){}
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
//打印int数组元素
void printIntArr(MyArr<int>& arr){
for (int i = 0; i < arr.getSize(); ++i) {
cout << arr[i] << endl;
}
}
//打印自定义数据类型数组元素
void printPersonArr(MyArr<Person>& arr){
for (int i = 0; i < arr.getSize(); ++i) {
cout << arr[i].mName << endl;
cout << arr[i].mAge << endl;
}
}
int main(){
MyArr<int>arr1(5);//测试1
for (int i = 0; i < 5; ++i) {
arr1.pushBack(i);//尾插法向数组添加数据
}
printIntArr(arr1);
cout << "arr1容量" << arr1.getCapa() << endl;
cout << "arr1大小" << arr1.getSize() << endl;
MyArr<int>arr2(arr1);//测试2
printIntArr(arr2);
arr2.popBack();
cout << "arr2容量" << arr2.getCapa() << endl;//输出 5
cout << "arr2大小" << arr2.getSize() << endl;//输出 4
MyArr<int>arr3(100);//测试3
arr3 = arr1;//测试4
//测试1、2、3、4行的输出:
//有参构造函数调用
//拷贝构造函数调用
//有参构造函数调用
//operator调用
//析构函数调用
//析构函数调用
//析构函数调用
//——————————测试自定义数据类型——————————
MyArr<Person> arr(10);
Person p1("小A", 10);
Person p2("小B", 15);
Person p3("小C", 13);
Person p4("大D", 16);
Person p5("大E", 35);
Person p6("小A", 8);
//将数据插入到数组中
arr.pushBack(p1);
arr.pushBack(p2);
arr.pushBack(p3);
arr.pushBack(p4);
arr.pushBack(p5);
arr.pushBack(p6);
//打印数组
printPersonArr(arr);
cout << "arr容量" << arr.getCapa() << endl;//输出arr容量10
cout << "arr大小" << arr.getSize() << endl;//输出arr大小6
//——————————测试完成——————————
}
二、STL基础
STL:标准模板库,广义上分为容器、算法和迭代器,容器和算法之间通过迭代器连接,几乎所有的代码都采用多了模板类或模板函数。
(一)STL六大组件
(1)容器:各种数据结构,如vector、list、deque、set、map,用来存放数据;
(2)算法:各种常用的算法,如sort、find、copy、for_each等;
(3)迭代器:扮演了容器与算法之间的胶合剂;
(4)仿函数:行为类似函数,可作为算法的某种策略;
(5)适配器:一种用来修饰同期或者仿函数或迭代器借口的东西;
(6)空间配置器:负责空间的配置与管理。
(二)容器、算法、迭代器
(1)容器就是将运用最广泛的一些数据结构(数组、链表、树、栈、队列、集合、映射表)实现出来,这些容器分为序列式容器和关联式容器两种:
- 序列式容器:强调值的排序,每个元素均有固定的位置;
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
(2)算法分为:
-
质变算法:指运算过程中会更改区间内的元素的内容,如拷贝、替换、删除;
-
非质变算法:指运算过程中不会更改区间内的元素内容,如查找、计数、遍历、寻找极值等。
(3)迭代器提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露容器内部表示方式。每个容器有自己专属的迭代器,使用类似于指针。
迭代器种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双向迭代器(常用) | 读写操作,并能向前和先后操作 | 读写,支持++、– |
随机访问迭代器(常用) | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、–、[n]、-n、<、<=、>、>= |
1.容器:verctor
(1)三种方式遍历容器中的数据:
int main() {
//创建一个vector容器数组
vector<int> v;
v.push_back(1);
v.push_back(4);
v.push_back(2);
//第一种遍历方式:通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin();//起始迭代器,指向容器中第一个元素
vector<int>::iterator itEnd = v.end();//结束迭代器,指向容器中的最后一个元素的下一个位置
while (itBegin != itEnd){
cout << *itBegin <<endl;
itBegin ++;
}
//第二种遍历方式:通过for循环
for (vector<int>::iterator it = v.begin();it != v.end();it++){
cout << *it << endl;
}
//第三种遍历方式:利用自带的for_each函数
for_each(v.begin(),v.end(),printInfo);
return 0;
}
2.vector存放自定义数据类型
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
//——————————vector存放自定义数据类型并遍历——————————
void myDataType(){
vector<Person> v;
Person p1("小明",12);
Person p2("小花",16);
Person p3("小蓝",13);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
//遍历
for (vector<Person>::iterator it = v.begin();it != v.end();it ++){
cout << "姓名:" << (*it).mName << " 年龄:" << (*it).mAge << endl;//通过解引用方式
cout << "姓名:" << it->mName << " 年龄:" << it->mAge << endl;//通过指针方式
}
}
//——————————存放自定义数据类型指针并遍历——————————
void myDataTypePoint(){
vector<Person*> v;
Person p1("a",12);
Person p2("b",16);
Person p3("c",13);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
//遍历
for (vector<Person*>::iterator it = v.begin();it != v.end();it ++){
cout << "姓名:" << (**it).mName << " 年龄:" << (**it).mAge << endl;//通过解引用方式
cout << "姓名:" << (*it)->mName << " 年龄:" << (*it)->mAge << endl;//通过指针方式
}
}
3.vector嵌套vector
int main(){
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++){
//(*it)是容器vector<int>
for(vector<int>::iterator vit = (*it).begin();vit != (*it).end();vit++){
cout << *vit << " ";
}
cout << endl;
}
}
三、STL常用容器
(一)string容器
1.string基本概念
string本质上是一个类,和char* 的区别是:char是一个指针,string是一个类,类内部封装了char管理这个字符串,是一个char类型的容器。string内部封装了很多成员方法,如find、copy、delete、replace、insert等。string管理char所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责。str.size()返回字符串长度。
2.string构造函数
构造函数的原型:
- string:创建一个空的字符串
- string(const char* s):使用字符串s初始化
- string(const string& str):使用一个string对象初始化另一个string对象
- string(int n, char c):使用n个字符c初始化
//第1种
string s1;
//第2种
const char * str = "wzh";
string s2(str);
cout << "s2:" << s2 << endl;
//第3种
string s3(s2);
cout << "s3:" << s2 << endl;
//第4种
string s4(10, 'a');
cout << "s4:" << s4 << endl;//输出10个a
3.string赋值操作
给string字符串赋值的函数原型:
- string& operator=(const char* s); //char*类型的字符串赋值给当前字符串
- string& operator=(const string &s); //把字符串s赋给当前字符串
- string& operator=(char 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赋给当前字符串
4.字符串拼接
在string字符串末尾拼接字符串的函数原型:
- string& operator+=(const char* str); //重载+=运算符
- string& operator+=(const char c); //重载+=运算符
- string& operator+=(const string& str); //重载+=运算符
- string& append(const char *s); //把字符串s连接到当前的字符串末尾
- string& append(const char *s, int n); //把字符串s的前n个字符连接到当前的字符串末尾
- string& append(const string &s); //同operator+=(const string& str)
- string& append(const string &s, int pos, int n); //字符串s中中pos开始的n个字符连接到字符串结尾
5.字符串查找和替换
函数原型:
- 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
6.字符串的比较
按照ASCII进行比较,=返回0,>返回1,<返回-1.
函数原型:
- int compare(const string& s) const; //与字符串s比较
- int compare(const char *s) const; //与字符串s比较
7.字符存取
string中单个字符存取:
- char& operator[] (int n);//通过[]方式取字符
- char& at(int n); //通过at方法获取字符
8.字符串插入和删除
函数原型:
- string& insert(int pos, const char* s); //插入字符串
- string& insert(int pos, const string& str); //插入字符串
- string& insert(int pos, int n, char c); //在指定位置插入n个字符
- string& erase(int pos, int n = npos); //删除从pos开始的n个字符
9.子串获取
函数原型:
string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串
(二)vector容器
1.vector基本概念
vector数据结构和数组非常相似,也称为单端数组。与普通数组不同在于数组是静态空间,而vector可以动态扩展,即并不是在原空间之后续接新空间,而是找更大的内存空间,将原有数据拷贝到新空间,释放原空间,在插入数据过程中会不断开辟新空间,可以利用reserve()预留空间。vector容器的迭代器是支持随机访问的迭代器。
2.vector构造函数
vector<T> v;
//采用模板实现类实现,默认构造函数vector(v.begin(), v.end())
//将v[begin(), end())区间(前闭后开)中的元素拷贝给自身vector(n, elem);
//构造函数将n个elem拷贝给自身vector(const cector &vec)
//拷贝构造函数
void printVector(vector<int>&v){
for (vector<int>::iterator it = v.begin();it != v.end(); it++){
cout << *it << endl;
}
cout << endl;
}
int main(){
//1.默认构造:无参构造
vector<int>v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
}
printVector(v1);
//2.通过区间方式构造
vector<int>v2(v1.begin(), v1.end());
printVector(v2);
//3.n个elem方式构造
vector<int>v3(10, 100);
printVector(v3);//输出10个100
//4.拷贝构造
vector<int>v4(v3);
printVector(v4);
}
3.vector赋值操作
函数原型:
vector& operator=(const vector &vec);
//重载等号操作符assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给自身assign(n, elem);
//将n个elem拷贝赋值给自身
vector<int>v1;
for (int i = 0; i < 10; ++i) {
v1.push_back(i+1);
}
printVector(v1);
//1.operator=赋值
vector<int>v2;
v2 = v1;
//2.assign赋值
vector<int>v3;
v3.assign(v1.begin(), v1.end());
//3.assign进行n个elem赋值
vector<int>v4;
v4.assign(3,20);
4.vector容量和大小
函数原型:
empty();
//判断容器是否为空capacity();
//容器的容量size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分resize(int num, elem);
//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分
5.vector的插入和删除
函数原型:
push_back(ele);
//尾部插入元素elepop_back();
//删除最后一个元素insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素eleinsert(const_iterator pos, intcount, ele);
//迭代器指向位置pos插入count个元素eleerase(const_iterator start, const_iterator end);
//删除迭代器指向的元素erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素clear();
//删除容器中所有元素
6.vector数据存取
函数原型:
at(int idx);
//返回索引idx所指的数据operator[]
//返回索引idx所指的数据,直接使用[]返回数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
7.vector互换容器
函数原型:swap(vec);
//将vec与本身的元素互换
通过创建匿名对象可以用来收缩vector占用的空间:vector<int>(v).swap(v)
8.vector预留空间
通过预留空间减少vector动态扩展时的扩展次数。
函数原型:reserve(int n);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。
(三)deque容器
1.deque基本概念
deque是双端数组。vector对于头部的插入删除效率低,数据量越大效率越低。而deque对头部的插入删除速度快,但vector访问元素的速度快。
2.deque构造函数
函数原型:
deque<T>
//默认构造形式deque(beg, end);
//拷贝函数将[beg, end)区间的元素拷贝给自身deque(n ,elem);
//构造函数将n个elem拷贝给自身deque(const deque &deq);
//拷贝构造函数
void printDeque(const deque<int>&d){//打印不需要修改值,加const限制只读
for(deque<int>::const_iterator it = d.begin();it != d.end();it ++){
cout << *it << " ";
}
cout << endl;
}
int main(){
//第一种
deque<int>d1;
for (int i = 0; i < 10; ++i) {
d1.push_back(i);
}
printDeque(d1);
//第二种
deque<int>d2(d1.begin(), d1.end());
printDeque(d2);
//第三种
deque<int>d3(10,3);
printDeque(d3);
//第四种
deque<int>d4(d3);
printDeque(d4);
}
3.deque赋值操作
deque& operatot=(const deque &deq);
//重载等号操作符assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给自身assign(n, elem);
//将n个elem拷贝赋值给自身
4.deque大小操作
函数原型:
empty();
//判断容器是否为空size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分resize(int num, elem);
//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分
5.deque插入和删除
函数原型:
两端插入操作:
-
push_back(elem);
//尾部插入元素elem -
push_front(elem);
//头部插入元素elem -
pop_back();
//删除最后一个元素 -
pop_front();
//删除第一个元素指定位置操作:
-
insert(pos, elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置 -
insert(pos, n, elem);
//在pos位置插入n个元素elem,无返回值 -
insert(pos, beg, end);
//在pos位置插入[begin, end)的数据,无返回值 -
clear();
//删除容器中所有元素 -
erase(beg, end);
//删除[beg, end)区间的数据,返回下一个数据的位置 -
erase(pos);
//删除pos位置的数据,返回下一个数据的位置
6.deque数据存取
函数原型:
at(int idx);
//返回索引idx所指的数据operator[]
//返回索引idx所指的数据,直接使用[]返回数据front();
//返回容器中第一个数据元素back();
//返回容器中最后一个数据元素
7.deque排序
sort(iterator beg, iterator end)
//对beg和end区间内元素排序
排序默认升序排,对于支持随机访问的迭代的的容器,都可以用sort算法直接排序。
(四)stack容器
1.基本概念
stack:栈,是先进后出的容器,只有一个出口。只有顶端的元素可以使用,所以不允许有遍历行为。
2.常用接口
(1)构造函数:
stack<T> stk;
//stack采用模板类实现,stack对象的默认构造形式stack(const stack &stk);
//拷贝构造函数
(2)赋值操作:
stack& operator=(const stack &stk);
//重载等号操作符
(3)数据存取:
push(elem);
//向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素
(4)大小操作:
empty();
//判断堆栈是否为空size();
//返回栈的大小
(五)queue容器
1.基本概念
先进先出的队列,从一端新增,另一端移除元素。队列中只有队头和队尾可以被使用,因此不允许有遍历行为。
2.常用接口
(1)构造函数:
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式queue(const queue &que);
//拷贝构造函数
(2)赋值操作:
queue& operator=(const queue &que);
//重载等号操作符
(3)数据存取:
push(elem);
//向队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一个元素front();
//返回第一个元素
(4)大小操作:
empty();
//判断队列是否为空size();
//返回队列的大小
(六)list容器
1.list基本概念
list(链表):将数据进行链式存储,由一系列结点组成,结点包括存储数据元素的数据域和存储下一个结点地址的指针域组成。STL中的链表是双向循环链表。
链表优缺点:
可以快速插入和删除元素,并且删除和插入数据不会造成迭代器的失效。采用动态存储分配,不会内存浪费或溢出。但遍历速度没有数组快,且占用空间比数组大。
2.list构造函数
函数原型:
list<T> lst
//默认构造形式list(beg, end);
//拷贝函数将[beg, end)区间的元素拷贝给自身list(n ,elem);
//构造函数将n个elem拷贝给自身list(const list &lst);
//拷贝构造函数
void printList(const list<int>&l){
for(list<int>::const_iterator it = l.begin();it != l.end(); it ++){
cout << *it << " ";
}
cout << endl;
}
int main(){
//默认构造
list<int>l1;
l1.push_back(1);
l1.push_back(3);
l1.push_back(4);
printList(l1);
//区间方式构造
list<int>l2(l1.begin(), l1.end());
printList(l2);
//拷贝构造
list<int>l3(l2);
printList(l3);
//n个elem
list<int>l4(10,20);
printList(l4);
}
3.list赋值和交换
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给自身assign(n, elem);
//将n个elem拷贝赋值给自身list& operator=(const list &lst);
//重载等号运算符swap(lst);
//将lst与本身的元素互换
4.list大小操作
函数原型:
empty();
//判断容器是否为空size();
//返回容器中元素的个数resize(int num);
//重新指定容器的长度为num,容器变长则以默认值0填充新位置,变短则删除超出部分resize(int num, elem);
//重新制定容器长度num,变长则以elem填充新位置,变短则删除超出部分
5.list插入和删除
函数原型:
两端操作:
-
push_back(elem);
//尾部插入元素elem -
push_front(elem);
//头部插入元素elem -
pop_back();
//删除最后一个元素 -
pop_front();
//删除第一个元素指定位置操作:
-
insert(pos, elem);
//在pos位置(迭代器)插入一个elem元素的拷贝,返回新数据的位置 -
insert(pos, n, elem);
//在pos位置插入n个元素elem,无返回值 -
insert(pos, beg, end);
//在pos位置插入[begin, end)的数据,无返回值 -
clear();
//删除容器中所有元素 -
erase(beg, end);
//删除[beg, end)区间的数据,返回下一个数据的位置 -
erase(pos);
//删除pos位置的数据,返回下一个数据的位置 -
remove(elem);
//移除容器中所有与elem值匹配的元素
6.list数据存取
函数原型:
front();
//返回第一个元素back();
//返回最后一个元素
7.list逆序和排序
reverse();
//逆序链表sort();
//链表排序,所有不支持随机访问迭代器的容器,不能用标准算法,内部提供此sort算法
若降序排列,则:
bool compare(int v2, int v2){
return v1 > v2;//降序排列,则让v1>v2
}
lst.sort(compare);
8.排序案例
class Persons{
public:
Persons(string name, int age, int height){
this->mName = name;
this->mAge = age;
this->mHeight = height;
}
string mName;
int mAge;
int mHeight;
};
void printPerson(list<Persons>&p){
for(list<Persons>::iterator it = p.begin();it != p.end(); it ++){
cout << "姓名:" << it->mName << " 年龄:" << it->mAge << " 身高:" << it->mHeight << endl;
}
}
//指定排序规则,按年龄升序,年龄相同时按照身高降序排列
bool compare(Persons &p1, Persons &p2) {
if (p1.mAge == p2.mAge) {
return p1.mHeight > p2.mHeight;
} else {
return p1.mAge < p2.mAge;
}
}
int main(){
list<Persons>l;
Persons p1("小花", 23, 171);
Persons p2("小蓝", 12, 155);
Persons p3("小紫", 12, 165);
Persons p4("小绿", 34, 174);
Persons p5("小黑", 12, 130);
l.push_back(p1);
l.push_back(p2);
l.push_back(p3);
l.push_back(p4);
l.push_back(p5);
//打印数据
cout << "————————排序前:————————" << endl;
printPerson(l);
//按照年龄进行升序排列
l.sort(compare);
cout << "————————排序后:————————" << endl;
printPerson(l);
}
(七)set/multiset容器
1.基本概念
所有元素在插入时自动排序,属于关联式容器,底层结构使用二叉树实现。
set和multiset区别:
- set中不允许容器中有重复的元素
- multiset允许容器中有重复的元素
2.set构造函数和赋值
构造:
set<T> st;
//默认构造函数set<const set &st>;
//拷贝构造函数
赋值:
set& operator=(const set &st);
//重载等号操作符
3.set大小和交换
函数原型:
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换两个集合容器
4.set插入和删除
函数原型:
insert(elem);
//在容器中插入元素clear();
//清空容器元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器erase(elem);
//删除容器中值为elem的元素
5.set查找和统计
函数原型:
find(key);
//查找key是否存在,存在返回该元素的迭代器,不存在返回set.end()count(key);
//统计key的元素个数:对set只能是0或1
6.set和multiset的区别
-
set不能插入重复数据,multiset可以
-
set插入数据的同时会返回插入结果,使用对组pair:
-
pair<set<int>::iterator, bool> ret = st.insert(1); if(ret.second){ cout << "插入成功!"; }
-
multiset不会检测数据,可以重复插入数据
7.pair对组创建
成对出现的数据,利用对组可以返回两个数据。有两种创建方式:
pair<type, type> p (value1, value2);
pair<type, type> p = make_pair(value1, value2);
//第一种方式
pair<string, int>p("wzh", 25);
//第二种方式
pair<string, int>p2 = make_pair("wzh", 24);
cout << p.first << p.second;
8.set容器排序
set容器默认从小到大排序,利用仿函数可以改变排序规则。自带数据类型可以不指定排序规则,自定数据类型必须指定。
(1)自带数据类型:
//使用仿函数指定规则
class Compares{
public:
bool operator()(int v1, int v2){
return v1 > v2;
}
};
int main(){
set<int>s1;
s1.insert(6);
s1.insert(1);
s1.insert(3);
s1.insert(2);
s1.insert(4);
for(set<int>::iterator it = s1.begin();it != s1.end();it ++){
cout << *it << " ";
}
//指定排序规则为降序
set<int, Compares>s2;//指定仿函数
s2.insert(6);
s2.insert(1);
s2.insert(3);
s2.insert(2);
s2.insert(4);
for(set<int, Compares>::iterator it = s2.begin();it != s2.end();it ++){
cout << *it << " ";
}
}
(2)自定义数据类型:
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
class SetCompare{
public:
bool operator()(const Person&p1, const Person&p2){
return p1.mAge > p2.mAge;
}
};
int main(){
//————————————自定义数据类型————————————
set<Person, SetCompare>s;
Person p1("大B",20);
Person p2("小C",12);
Person p3("大A",25);
Person p4("小D",11);
//s.insert(p1);自定义数据类型不能按照默认升序排,需要自定仿函数排序规则
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
//输出
for(set<Person>::iterator it = s.begin();it != s.end();it ++){
cout << "姓名:" << it->mName << " 年龄:" << it->mAge << endl;
}
}
(八)map/multimap容器
1.基本概念
- map中所有元素都是pair
- pair中第一个元素为key,起索引作用,value是实职
- 所有元素根据元素的键值自动排序
map/multimap属于关联式容器,底层结构使用二叉树实现。map不允许有重复值,multimap允许有重复值。
2.map构造和赋值
构造:
map<T1, T2>mp;
//map默认构造函数map(const map &mp);
//拷贝构造函数
赋值:
map& operator=(const map &mp);
//重载等号操作符
map<int, int>mp;
mp.insert(pair<int ,int>(5, 14));
mp.insert(pair<int ,int>(3, 10));
mp.insert(pair<int ,int>(7, 8));
mp.insert(make_pair(4, 20));
for(map<int, int>::iterator it = mp.begin();it != mp.end();it ++){
cout << "key:" << (*it).first << " value:" << it->second << endl;
}
3.map大小和交换
函数原型:
size();
//返回容器中元素的数目empty();
//判断容器是否为空swap(st);
//交换两个集合容器
4.map插入和删除
函数原型:
insert(elem);
//在容器中插入元素clear();
//清空容器元素erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器erase(beg, end);
//删除区间的所有元素,返回下一个元素的迭代器erase(key);
//删除容器中键值为key的键值对
可以用key[xx]的方式获取键值对。
5.map查找和统计
函数原型:
find(key);
//查找key是否存在,存在返回该键的元素的迭代器,不存在返回map.end()count(key);
//统计key的元素个数:对map只能是0或1
6.map容器排序
map容器默认排序规则是按key值升序排列,利用仿函数可以改变排序规则,对于自定义数据类型,map必须制定排序规则:
class compare{
public:
bool operator()(int v1, int v2){
return v1 > v2;//降序
}
}
7.员工分组信息显示案例:
class Worker{
public:
string mName;
int mSalary;
};
//创建员工
void create(vector<Worker>&v){
string nameSeed = "ABCDEFGHIJ";
for (int i = 0; i < 10; ++i) {
Worker worker;
worker.mName = "员工";
worker.mName += nameSeed[i];
worker.mSalary = rand()%10000 + 10000;//10000~19999
v.push_back(worker);
}
}
//员工分组
void group(vector<Worker>&v, multimap<int, Worker>&mp){
for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
//产生随机部门编号
int depId = rand()%3;//0 1 2
//将员工插入到分组中
mp.insert(make_pair(depId, *it));
}
}
//打印员工姓名和工资
void printVector(vector<Worker>&v){
for(vector<Worker>::iterator it = v.begin();it != v.end();it ++){
cout << "姓名:" << it->mName << " 工资:" << it->mSalary << endl;
}
}
//分组打印员工信息
void printWorkerOfGroup(multimap<int, Worker>&mp){
multimap<int, Worker>::iterator it;
for (int i = 0; i < 3; ++i) {//循环打印三个部门的信息
cout << "部门" << i << ":" << endl;
it = mp.find(i);
int count = mp.count(i);
int index = 0;
for(;it != mp.end() && index < count;it ++, index ++){
cout << "部门:" << it->first << " 姓名:" << it->second.mName
<< " 工资:" << it->second.mSalary <<endl;
}
}
}
int main(){
vector<Worker>v;
create(v);//创建员工
printVector(v);//输出员工信息
cout << "-----------------------" << endl;
multimap<int, Worker>mp;
group(v, mp);//员工分组
printWorkerOfGroup(mp);//分组显示员工的部门、姓名、工资
}
四、函数对象
(一)函数对象(仿函数)
1.函数对象概念
-
重载函数调用操作符的类,其对象称为函数对象
-
函数对象使用重载的()时,行为类似函数调用,也叫仿函数
-
函数对象(仿函数)是一个类,不是函数
2.函数对象使用
特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参、有返回值
- 函数对象超出普通函数的概念,函数对象内部可以有自己的状态(成员属性等)
- 函数对象可以作为参数传递
class MyAdd{
public:
//1.使用时像普通函数,可有参 有返回
int operator()(int v1, int v2){
return v1 + v2;
}
};
//2.函数对象内部可以有自己的状态(成员属性)
class MyPrint{
public:
MyPrint(){
this->count = 0;
}
void operator()(string test){
cout << test << endl;
this->count ++;
}
int count;//内部自己的状态,记录函数被调用次数
};
//3.函数对象可以作为参数传递
void doPrint(MyPrint &mp, string test){
mp(test);
}
int main(){
MyAdd myAdd;//1.这是函数对象
cout << myAdd(1, 5);
//2.打印调用次数
MyPrint myPrint;
myPrint("yeah!");
myPrint("yeah!");
myPrint("yeah!");
myPrint("yeah!");
cout << myPrint.count << endl;
//3.作为对象传递
MyPrint myPrint1;
doPrint(myPrint1,"hello~~");
}
(二)谓词
1.谓词概念
- 返回bool类型的仿函数称为谓词
- 如果operator()接收一个参数为一元谓词,接收2个参数为二元谓词
2.谓词示例
class FiveBig{//仿函数,查找大于5的数
public:
bool operator()(int val){//bool类型,一个参数
return val > 5;
}
};
class Compare{
public:
bool operator()(int v1, int v2){
return v1 > v2;
}
};
int main(){
vector<int>v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
//创建匿名对象
vector<int>::iterator it = find_if(v.begin(), v.end(),FiveBig());
if (it == v.end()){
cout << "未找到" << endl;
}else{
cout << "找到:" << *it << endl;
}
vector<int>v2;
v2.push_back(4);
v2.push_back(1);
v2.push_back(3);
sort(v2.begin(), v2.end(), Compare());
for(vector<int>::iterator it = v2.begin();it != v2.end();it++){
cout << *it << " ";
}
}
(三)内建函数对象
1.意义
STL内置了一些函数对象,包括算术仿函数、关系仿函数、逻辑仿函数,这些仿函数所产生的对象,用法和一般函数完全相同,使用时引入头文件functional
2.算数仿函数
实现size运算,negate是一元运算,其他为二元。
仿函数原型:
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一元仿函数
negate<int>n;
int ret = n(3);
cout << ret << endl;//输出-3
//加法 二元仿函数
plus<int>p;//只允许两个相同类型
cout << p(4,6) << endl;
3.关系仿函数
仿函数原型:
template<class T> bool equal_to<T>
//等于template<class T> bool not_equal_to<T>
//不等于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>
//小于等于
int main(){
vector<int>v;
v.push_back(5);
v.push_back(2);
v.push_back(1);
//使用关系仿函数greater 降序排列
sort(v.begin(), v.end(), greater<int>());
}
4.逻辑仿函数(用途少)
template<class T> bool logica_and<T>
//逻辑与template<class T> bool logical_or<T>
//逻辑或template<class T> bool logical_not<T>
//逻辑非
vector<bool>v1;
v1.push_back(true);
v1.push_back(false);
//将v1中元素取反后到v2中
vector<bool>v2;
v2.resize(v1.size());//先开辟空间,再搬运
transform(v1.begin(), v1.end(), v2.begin(),logical_not<bool>());
五、STL常用算法
概述:
- 算法主要由头文件
<algorithm>
、<functional>
、<numeric>
组成 <algorithm>
是STL所有头文件中最大的,包括比较、交换、查找、遍历、复制、修改等<numeric>
体积很小,只包括几个在序列上进行简单数学运算的模板函数<functional>
定义了一些模板类,用以函数声明对象
(一)常用遍历算法
for_each();
//遍历容器transform();
//搬运容器到另一个容器中
1.for_each算法(常用)
函数原型:
for_each(iterator beg, iterator end, _func);
//开始/结束迭代器beg/end,_func函数或函数对象
//普通函数,打印输出
void printVec(int val){
cout << val << " ";
}
//仿函数,打印输出
class PrintFunc{
public:
void operator()(int val){
cout << val << " ";
}
};
int main(){
vector<int>v;
v.push_back(2);
v.push_back(1);
v.push_back(3);
for_each(v.begin(), v.end(), printVec);//普通函数
for_each(v.begin(), v.end(), PrintFunc());//仿函数
}
2.transform算法
搬运容器到另一个容器中。
函数原型:
transform(iterator beg1, iterator end1,iterator beg2, _func);
//原容器开始和结束迭代器;目标容器开始迭代器;_func函数或函数对象
class Transform{
public:
int operator()(int val){
return val + 10;//搬运过程中可以进行运算
}
};
class PrintFun{
public:
void operator()(int val){
cout << val << " ";
}
};
int main(){
vector<int>v;
v.push_back(3);
v.push_back(1);
v.push_back(2);
vector<int>vTarget;
vTarget.resize(v.size());//搬运前提前开辟空间
transform(v.begin(), v.end(), vTarget.begin(),Transform());
for_each(vTarget.begin(), vTarget.end(), PrintFun());
}
(二)常用查找算法
find();
//查找元素find_if();
//按条件查找元素adjacent_find();
//查找相邻重复元素binary_search();
//二分查找法count();
//统计元素个数count_if();
//按条件统计元素个数
1.find算法
查找指定元素,找到返回指定元素的迭代器,找不到返结束迭代器。
函数原型:
find(iterator beg, iterator end, value);
//beg/end:开始/结束迭代器,value查找的元素
示例:查找内置数据类型和自定义数据类型
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
//重载==,底层find函数才知道如何对比Person类型
bool operator==(const Person &p){
if(this->mName == p.mName && this->mAge == p.mAge){
return true;
} else{
return false;
}
}
string mName;
int mAge;
};
int main(){
//——————————查找内置数据类型————————————
vector<int>v;
v.push_back(5);
v.push_back(1);
v.push_back(3);
vector<int>::iterator it = find(v.begin(), v.end(), 5);
if(it == v.end()){
cout << "没找到"<< endl;
} else{
cout << "找到:" << *it << endl;
}
//——————————查找自定义数据类型————————————
vector<Person>vPerson;
Person p1("小李", 20);
Person p2("小刘", 14);
Person p3("小孙", 34);
vPerson.push_back(p1);
vPerson.push_back(p2);
vPerson.push_back(p3);
//下面的find需要重载==运算符
vector<Person>::iterator its = find(vPerson.begin(), vPerson.end(), p2);
if(it == v.end()){
cout << "没找到!" << endl;
} else{
cout << "找到:" << its->mName << its->mAge << endl;
}
}
2.find_if算法
按条件查找元素,找到返回指定元素的迭代器,找不到返结束迭代器。
函数原型:
find_if(iterator beg, iterator end, _Pred);
//beg/end:开始/结束迭代器,_Pred:函数或者谓词(返回bool类型的仿函数)
示例:条件查找内置数据类型和自定义数据类型
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
class GreaterFive{//谓词,大于5
public:
bool operator()(int val){
return val > 5;
}
};
class AgeGreater20{//谓词,年龄大于20
public:
bool operator()(const Person &p){
return p.mAge >20;
}
};
int main(){
//——————————条件查找 内置数据类型————————————
vector<int>v1;
v1.push_back(5);
v1.push_back(6);
v1.push_back(3);
vector<int>::iterator it1 = find_if(v1.begin(), v1.end(),GreaterFive());
if(it1 == v1.end()){
cout << "没找到"<< endl;
} else{
cout << "找到大于5的数:" << *it1 << endl;
}
//——————————条件查找 自定义数据类型————————————
vector<Person>v2;
Person p1("小李", 20);
Person p2("小刘", 14);
Person p3("小孙", 34);
v2.push_back(p1);
v2.push_back(p2);
v2.push_back(p3);
vector<Person>::iterator it2 = find_if(v2.begin(), v2.end(), AgeGreater20());
if(it2 == v2.end()){
cout << "没找到!" << endl;
} else{
cout << "找到:" << it2->mName << it2->mAge << endl;
}
}
3.adjacent_find算法
查找相邻重复元素,返回相邻元素的第一个位置的迭代器。
函数原型:
adjacent_find(iterator beg, iterator end);
//beg/end:开始/结束迭代器
int main(){
//——————————条件查找 内置数据类型————————————
vector<int>v;
v.push_back(0);
v.push_back(3);
v.push_back(3);
v.push_back(1);
v.push_back(3);
vector<int>::iterator pos = adjacent_find(v.begin(),v.end());
if (pos == v.end()){
cout << "未找到相邻重复元素!" << endl;
} else{
cout << "找到相邻重复元素:" << *pos << endl;
}
}
4.binary_search算法
二分查找法,查找指定元素是否存在,存在返回true,不存在返回false。在无序序列中不可用,无序序列中查找结果未知。
函数原型:
bool binary_search(iterator beg, iterator end,value);
//beg/end:开始/结束迭代器,value:查找的元素
int main() {
//——————————二分查找,容器为有序序列————————————
vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
bool ret = binary_search(v.begin(), v.end(),9);
if (ret) {
cout << "找到元素!" << endl;
} else {
cout << "未找到相邻重复元素!" << endl;
}
}
5.count算法
统计元素个数。
函数原型:
count(iterator beg, iterator end, value);
示例:统计内置数据类型和自定义数据类型
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
//重载==,底层count函数才知道如何对比Person类型
bool operator==(const Person &p){
if(this->mAge == p.mAge){
return true;
} else{
return false;
}
}
string mName;
int mAge;
};
int main() {
//——————————1.查找内置数据类型————————————
vector<int> v;
v.push_back(2);
v.push_back(3);
v.push_back(1);
v.push_back(3);
int num1 = count(v.begin(), v.end(),3);
cout << num1 << endl;
//——————————2.查找自定义数据类型————————————
vector<Person>v2;
Person p1("二哥", 20);
Person p2("三哥", 14);
Person p3("大哥", 34);
Person p("大A",14);//用于检测
v2.push_back(p1);
v2.push_back(p2);
v2.push_back(p3);
int num2 = count(v2.begin(), v2.end(), p);
cout << "和大A同岁的人个数" << num2 << endl;
}
6.count_if算法
按条件统计元素个数。
函数原型:
count_if(iterator beg, iterator end, _Pred);
//_Pred:谓词
示例:条件统计内置数据类型和自定义数据类型的个数
class Person{
public:
Person(string name, int age){
this->mName = name;
this->mAge = age;
}
string mName;
int mAge;
};
class GreaterTen{//谓词,大于10
public:
bool operator()(int val){
return val > 10;
}
};
class AgeGreater20{//谓词,年龄大于20
public:
bool operator()(const Person &p){
return p.mAge >20;
}
};
int main(){
//——————————条件统计 内置数据类型————————————
vector<int>v1;
v1.push_back(9);
v1.push_back(20);
v1.push_back(22);
v1.push_back(11);
int num1 = count_if(v1.begin(), v1.end(),GreaterTen());
cout << "大于10的个数:"<< num1 << endl;
//——————————条件统计 自定义数据类型————————————
vector<Person>v2;
Person p1("小马", 12);
Person p2("小猪", 43);
Person p3("小牛", 34);
v2.push_back(p1);
v2.push_back(p2);
v2.push_back(p3);
int num2 = count_if(v2.begin(), v2.end(), AgeGreater20());
cout << "大于20岁的人员个数:"<< num2 << endl;
}
(三)常用排序算法
sort();
//对容器内元素进行排序random_shuffle();
//洗牌,指定范围内元素随机调整次序merge();
//容器元素合并,并存储到另一容器中reverse();
// 反转指定范围内的元素
1.sort算法
函数原型:
sort(iterator beg, iterator end, _Pred);
//_Pred谓词,可填可不填
2.random_shuffle算法
函数原型:
random_shuffle(iterator beg, iterator end);
3.merge算法
函数原型:
merge(iterator beg1, iterator end1, iterator beg2, iterator end2,iterator dest);
//dest:目标容器开始迭代器
void printVector(int val){
cout << val << " ";
}
int main() {
//——————————merge合并两个容器到另一个容器,必须为有序序列————————————
vector<int> v1;
vector<int> v2;
vector<int> vTarget;//目标容器
for (int i = 0; i < 10; ++i) {
v1.push_back(i);
v2.push_back(i+2);
}
//先给目标容器开辟空间
vTarget.resize(v1.size() + v2.size());
merge(v1.begin(), v1.end(), v2.begin(), v2.end(),vTarget.begin());
for_each(vTarget.begin(), vTarget.end(), printVector);
}
4.reverse算法
函数原型:
reverse(iterator beg, iterator end);```
(四)常用拷贝替换算法
copy();
//容器中指定范围的元素拷贝到另一容器中replace();
//容器中指定范围的旧元素更改为新元素replace_if();
//容器中指定范围的满足条件的元素替换为新元素swap();
//互换两个容器中的元素
1.copy算法
函数原型:
copy(iterator beg, iterator end, iterator dest);
//dest:目标起始迭代器,目标容器提前开辟空间
2.replace算法
函数原型:
replace(iterator beg, iterator end, oldvalue, newvalue);
3.replace_if算法
函数原型:
replace_if(iterator beg, iterator end, _Pred, newvalue);
void printVect(int val){
cout << val << " ";
}
class GreaterFive{//谓词,大于5
public:
bool operator()(int val){
return val > 5;
}
};
int main(){
vector<int>v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
replace_if(v.begin(), v.end(),GreaterFive(),100);
for_each(v.begin(), v.end(), printVect);
}
4.swap算法
swap(container c1, container c2);
//交换同类型的容器
(五)常用算数生成算法
算数生成算法是小型算法,在头文件<numeric>
中
accumulate();
//计算容器元素累计总和,返回intfill();
//向容器中添加元素
1.accumulate算法
accumulate(iterator beg, iterator end, value);
//value:起始累加值
2.fill算法
fill(iterator beg, iterator end, value);
//value:填充值
(六)常用集合算法
set_intersection();
//求两个容器的交集,放到另一个容器中set_union();
//求两个容器的并集set_difference();
//求两个容器的差集
三个函数的返回值是:新容器的最后位置的迭代器。2个原容器必须是有序序列。
1.set_intersection算法
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
目标容器开辟空间为小容器size()。
2.set_union算法
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
目标容器开辟空间为两容器和。
3.set_difference算法
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
目标容器开辟空间为大容器size()。