《黑马笔记》 --- C++ 提高编程

发布于:2025-08-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

C++ 提高编程

  • 本阶段主要针对C++泛型编程>和STL技术的讲解。

1. 模板

1.1 模板的概念

生活中的模板:一寸照片模板,ppt模板,还有求职过程中的简历模板,这可以帮我们在生活中方便很多,C++也是如此。

模板的特点:

  • 模板不可以直接使用,他只是一个框架
  • 模板的通用并不是万能的

1.2 函数模板

在c++中除了所学过的面向对象的思想之外

  • 还有另一种编程思想称为泛型编程,主要利用的技术就是模板。
  • c++提供两种模板机制:函数模板和类模板
1.2.1 函数模板语法

建立一个通用函数,其返回值和星形参类型不确定,用一个虚拟的类型来代表。

语法:

template<typename T>
函数声明和定义

template 声明创建模板

typename 表示其后面的符号是一种数据类型,也可以用class

T 通用的数据类型,名称可换,一般大写。

image-20250720095105664

上图所示:我们如果交换的数据类型不止是一个,那按照以前的做法,就得实现两个函数来交换,但是这种模板的思想,我们只需要写一个就好了,而在调用的时候,有两种方式:

  • 自动类型推导:编译器自己推导什么类型
  • 显示指定类型:我们告诉编译器什么类型
1.2.2 函数模板注意事项
  • 自动类型推导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定出T的数据类型,才可以使用

image-20250720101839752

1.2.3 函数模板案例

写了一个选择排序。

#include <iostream>

using namespace std;



template<typename T>
void mySwap(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}


template<typename T>
void mySort(T &arr, int size)
{
	for (int i = 0; i < size - 1; i++)
	{
		int maxIndex = i;
		for (int j = i + 1; j < size; j++)
		{
			if (arr[j] > arr[maxIndex])
				maxIndex = j;
		}

		mySwap(arr[i], arr[maxIndex]);
	}
}

void test01()
{
	int arr[] = { 1,200,30,400,50 };
	//char arr[] = "pkshdas";
	int k = sizeof(arr) / sizeof(arr[1]);
	mySort(arr, k);

	for (int i = 0; i < k; i++)
		cout << arr[i] << " ";
	
}

int main()
{
	
	test01();
	return 0;
}
1.2.4 普通函数和函数模板的区别
  • 普通函数调用可以发生隐式类型转换
  • 函数模板用自动类型推导,不可以发生隐士类型转化。
  • 函数模板用显示指定类型,可以发生隐式类型转化。

image-20250720112529193

上图中当普通函数调用的时候,会自动将char类型转化成int类型,10 + 97 = 107,而模板函数中的自动类型推导则不会自动转化。

对于显示类型指定,我感觉<int>就是将上述函数模板中T全部编程了int,所以他会和普通函数一样,进行隐式转换。

建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T。

1.2.5 普通函数与函数模板调用规则

普通函数与函数模板是可以发生重载的。

调用规则:

  1. 如果普通函数和函数模板都实现,优先调用普通函数。
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板。
#include <iostream>

using namespace std;



void myPrintf(int a, int b)
{
	cout << "普通函数的调用!" << endl;
}

template<typename T>
void myPrintf(T a, T b)
{
	cout << "函数模板的调用!" << endl;
}

template<typename T>
void myPrintf(T a, T b, T c)
{
	cout << "函数模板重载的调用!" << endl;
}


//1.如果普通函数和函数模板都实现,优先调用普通函数。
void test01()
{
	int a = 0, b = 0;
	myPrintf(a, b); 
}


//2. 可以通过空模板参数列表来强制调用函数模板
//3. 函数模板也可以发生重载
void test02()
{
	int a = 0, b = 0;
	myPrintf<>(a, b);

	myPrintf<>(a, b, 10);
}


//4.如果函数模板可以产生更好的匹配,优先调用函数模板
//因为如果调用普通函数是需要进行隐式类型转换的。 
void test04()
{
	char c1 = 'a', c2 = 'b';

	myPrintf(c1, c2);
}

int main()
{
	//test01();
	//test02();
	test04();
	return 0;
}

总结: 既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。

1.2.6 模板的局限性

模板的通用性并不是万能的,碰上自定义数据类型,一一般的模板就会出错了。

template<typename T>
bool myCompare(T a, T b)
{
	return a == b;
}


void test01()
{
	int a = 10, b = 20;
	char c1 = 'a', c2 = 'a';

	if (myCompare(a, b))
		cout << "a = b" << endl;
	else
		cout << "a != b" << endl;

	if (myCompare(c1, c2))
		cout << "c1 = c2" << endl;
	else
		cout << "c1 != c2" << endl;

}

这种比较是可以的,但是下面的比较就不行了。

void test02()
{
	Person p1("kxq", 18);
	Person p2("kxq", 18);


	if (myCompare(p1, p2))
		cout << "p1 = p2" << endl;
	else
		cout << "p1 != p2" << endl;
}

我们可以用之前所学的将==重载,但如果还要比较大于小于等等其他情况,需要重载的太多了,不太方便。

所以需要添加一个具体化的模板,就可以解决自定义类型的通用化了。

//----------------------------------------
template<> bool myCompare(Person a, Person b)
{
	return a.name == b.name && a.age == b.age;
}
//----------------------------------------

void test02()
{
	Person p1("kxq", 18);
	Person p2("kxq", 21);


	if (myCompare(p1, p2))
		cout << "p1 = p2" << endl;
	else
		cout << "p1 != p2" << endl;
}

1.3 类模板

1.3.1 类模板的语法

类模板的作用:

  • 建立一个通用类,类中成员数据类型可以不具体制定,用一个虚拟的类型来代表。

建立一个通用函数,其返回值和星形参类型不确定,用一个虚拟的类型来代表。

语法:

template<typename T>

template 声明创建模板

typename 表示其后面的符号是一种数据类型,也可以用class

T 通用的数据类型,名称可换,一般大写。

  • 类模板和函数模板十分像,只是在template下一行写成类就好了,示例如下:
#include <iostream>
#include <string>

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 << " 年龄: " << this->m_Age << endl;
	}

	NameType m_Name;
	AgeType m_Age;
};


void test01()
{
	Person<string, int> p1("kxq", 18);
	p1.showPerson();
}


int main()
{
	test01();
	return 0;
}
1.3.2 类模板与函数模板的区别
  • 类模板没有自动类型推导的使用方式
//1. 类模板没有自动类型推导使用方式
void test01()
{
	//这是错误的,编译器不能够自动推导。
	Person p1("xl", 18);

	//只有这一种方式可以。
	Person<string, int> p1("kxq", 18);
	p1.showPerson();
}


int main()
{
	test01();
	return 0;
}
  • 类模板在模板参数列表中可以有默认参数

image-20250720155940811

就是说我要是在模板参数列表中有了默认参数,在下面使用时候只需要写出另一个即可。

1.3.3 类模板成员函数创建时机。
  • 类模板成员函数在调用时才创建
#include <iostream>

//类模板成员函数创建时机
//类模板成员函数在调用时才创建

using namespace std;


class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}

};


class Person2
{
public:
	void showPerson2()
	{
		cout << "Person2 show" << endl;
	}

};
template<class T>
class MyClass
{
public:
	T obj;

	//类模板中的成员函数
	void fun1()
	{
		obj.showPerson1();
	}
	void fun2()
	{
		obj.showPerson2();
	}
};


void test01()
{
	MyClass<Person1> m;
	m.fun1();
	//m.fun2();	//错误。


	MyClass<Person2> mm;
	//mm.fun1();//错误。
	mm.fun2();
}


int main()
{
	test01();
	return 0;
}
  • 类模板成员函数并不是一开始就创建的,在调用时才去创建。
1.3.4 类模板对象做函数参数

学习:类模板实例化出对象,向函数传参的方式

一共有三种传入方式:

  • 指定传入的类型 — 直接显示对象的数据类型
  • 参数模板化 — 将对象中的参数变为模板进行传递
  • 整个类模板化 — 将这个对象类型 模板化进行传递

image-20250720170349751

代码示例:

#include <iostream>

using namespace std;



template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson()
	{
		cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
	}

	T1 m_Name;
	T2 m_Age;
};

//1. 指定传入的类型
void printfPerson1(Person<string, int> &p)
{
	p.showPerson();
}
void test01()
{
	Person<string, int> p("xl", 18);
	printfPerson1(p);
}
//2. 参数模板化
template<class T1, class T2>
void printfPerson2(Person<T1, T2> &p)
{
	p.showPerson();
	cout << "T1 类型: " << typeid(T1).name() << endl;
	cout << "T2 类型: " << typeid(T2).name() << endl;

}
void test02()
{
	Person<string, int> p("N9", 21);
	printfPerson2(p);
	
}
//3. 整个类模板化
template<class T>
void printfPerson3(T p)
{
	p.showPerson();
	cout << "T 类型: " << typeid(T).name() << endl;
}
void test03()
{
	Person<string, int> p("DBQ", 21);
	printfPerson3(p);
}

int main()
{
	test01();
	test02();
	test03();
	return 0;
}
1.3.5 类模板与继承

当类模板碰上继承时,需要注意以下几点:

  • 当子类继承的父类是一个类模板是,子类在声明的时候,要指定出父类中T的类型
  • 如果不指定,编译器无法给予子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也需要变为类模板

下图 当我们的父类是一个类模板的时候,正常的方式肯定是不可以的,需要在Base后面写出参数列表。

image-20250721084944821

template<class T>
class Base
{
public:

	T m_A;
};

//1. 当子类继承的父类是一个类模板是,子类在声明的时候,要指定出父类中T的类型
class Son1 :public Base<int>
{

};

//2. 如果想灵活指定出父类中T的类型,子类也需要变为类模板
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:

	T1 m_A;
};

总结: 如果父类是类模板,子类需要指定出父类中T的数据类型

1.3.6 类模板成员函数类外实现
  • 类模板中成员函数类外实现时,需要加上模板参数列表

image-20250721092932881

代码示例:

template<class T1, class T2>
class Person
{
public:
	Person(T1 name, T2 age);

	void showPerson();

	T1 m_Name;
	T2 m_Age;
};

template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
	cout << "姓名: " << this->m_Name << " 年龄: " << this->m_Age << endl;
}


void test01()
{
	Person<string, int> p("xl", 18);
	p.showPerson();
}
1.3.7 类模板分文件编写

因为在1.3.3类模板创建时机中学过,类模板成员函数在调用时才创建 所以他和普通的类函数份文件编写不一样。

  • 普通的类函数份文件编写只需要保护其.h文件就好了,而类模板则需要包含类模板函数具体实现的.cpp文件,这是第一种解决方式。
  • 第二种解决方式是将 .h文件和.cpp文件写在一起,改为后缀为.hpp的文件

主流的解决方法是第二种解决方法,将类模板成员函数写到一起,并改后缀为.hpp

1.3.8 类模板与友元

额。。。。。全局函数类内实现简单加个friend就好了。

额。。。。。全局函数类外实现。。。。。。。先让编译器知道有这个函数,但是知道这个函数前又必须让编译器知道那个Person类,因为函数中用到了Person类了。

套娃这一块./

左脚踩右脚这一块./

image-20250721110951300

#include <iostream>
#include <string>

using namespace std;

//让下面的知道Person
template<class T1, class T2>
class Person;

//全局函数 类外实现
template<class T1, class T2>
void printfPerson2(Person<T1, T2> p)
{
	cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

template<class T1, class T2>
class Person
{
	//全局函数 类内实现
	friend void printfPerson(Person<T1, T2> p)
	{
		cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
	}
	//全局函数 类外实现
	//加空模板的参数列表
	//如果全局函数是类外实现,需要让编译器提前知道 这函数的存在
	friend void printfPerson2<>(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("xl", 18);
	printfPerson(p);


	Person<string, int> p2("n9", 21);
	printfPerson2(p2);

}

int main()
{
	test01();
	return 0; 
}

建议全局函数做类内实现,用法简单,而且编译器可以直接识别。

2. STL初始

STL,全称为 Standard Template Library(标准模板库),是 C++ 标准库中非常重要且强大的部分。它提供了一系列通用的模板类和函数,让我们能够方便、高效地处理数据结构和算法问题。

STL分为六大组件:

  • 容器: 用来存放数据结构
  • 算法: 各种各样的算法
  • 迭代器:扮演了容器和算法的胶合剂,连接容器和算法,提供统一的访问方式。
  • 仿函数:行为类似函数,可以作为算法的某种策略
  • 适配器:修饰容器或者仿函数或迭代器接口的东西
  • 空间配置器:负责空间的配置管理

2.1 STL的三大组件

  • 容器(Containers)
    容器是用来存储和组织数据的类模板。常见的容器有:
    • 顺序容器:如 vector(动态数组)、list(双向链表)、deque(双端队列)
    • 关联容器:如 setmap(基于红黑树的平衡树结构)
    • 无序关联容器:如 unordered_setunordered_map(基于哈希表)
  • 算法(Algorithms)
    STL提供了大量的通用算法,如排序、查找、拷贝、合并、差集等。这些算法与容器无关,只要容器支持迭代器,就能使用算法。
  • 迭代器(Iterators)
    迭代器是连接容器和算法的桥梁,类似于通用指针,能够访问容器中的元素。不同容器支持不同类别的迭代器(输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器),为算法提供灵活的访问方式。

2.2 STL的特点

  • 泛型编程
    STL基于模板实现,能够支持任意类型的数据,极大地增强了代码的复用性和灵活性。
  • 高效性
    STL的实现高度优化,许多算法和容器在性能上都接近手写代码。
  • 易用性和可维护性
    使用STL能大幅减少代码量,避免重复造轮子,提高开发效率和代码的可维护性。
  • 标准化
    STL是C++标准库的组成部分,跨平台、稳定,广泛被使用和支持。

3. STL常用的容器

3.1 String容器

string 是C++的字符串,而string本质上是一个类。

  • char* 是一个指针
  • string 是一个类,类中封装了char* 管理了这个字符串,是一个char*型容器

特点:比起C语言来讲,不必担心越界问题以及'\0'问题

3.1.1 string构造函数

构造函数原型:

  • stirng(); 创建一个空的字符串
    string(const char* s) 利用字符串s初始化
  • string(const string& str); 利用string对象初始化另一个string对象
  • string(int n, char c); 使用n个字符c初始化

代码示例:

#include <iostream>
#include <string>

using namespace std;

/*
- `stirng();`	创建一个空的字符串
  `string(const char* s) `利用字符串s初始化
- `string(const string& str);` 利用string对象初始化另一个string对象
- `string(int n, char c);` 使用n个字符c初始化
*/


void test01()
{

	
	string s1;				//默认构造
		
	string s2("abcdef");
	cout << s2 << endl;
	

	string s3(s2);
	cout << s3 << endl;

	string s4(9, 'n');
	cout << s4 << endl;
}


int main()
{

	test01();
	return 0;
}

3.1.2 string赋值操作

image-20250723094624225

代码示例:

#include <iostream>
#include <string>

using namespace std;


void test01()
{
	//三种等号赋值
	string str1;
	str1 = "N9 is my idol!";
	cout << "str1 = " << str1 << endl;


	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;

	
	string str3;
	str3 = 'y';
	cout << "str3 = " << str3 << endl;


	//
	string str4;
	str4.assign("DBQ is my idol!");
	cout << "str4 = " << str4 << endl;

	string str5;
	str5.assign("DBQ is my idol!", 3);
	cout << "str5 = " << str5 << endl;


	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;


	string str7;
	str7.assign(9, 'N');
	cout << "str7 = " << str7 << endl;

}

int main()
{

	test01();
	return 0;
}

3.1.3 string字符串拼接
  • 实现字符串末尾拼接字符串

image-20250723100125762

代码示例:

#include <iostream>
#include <string>

using namespace std;

//string 字符串拼接操作。


void test01()
{

	//+= 号
	string str1 = "N9 ";
	str1 += "is my idol";
	cout << "str1 = " << str1 << endl;

	str1 += ':';
	cout << "str1 = " << str1 << endl;

	string str2 = "(十年前我是金枪王,十年后我还是金枪王)";
	str1 += str2;
	cout << "str1 = " << str1 << endl;

	//append
	string str3 = "I";
	str3.append(" love ");
	cout << "str3 = " << str3 << endl;

	str3.append("game abcdef", 4);
	cout << "str3 = " << str3 << endl;

	string str4 = " is cf.";
	//str3.append(str4);
	str3.append(str4, 3, 4);
	cout << "str3 = " << str3 << endl;


}
int main()
{
	test01();
	return 0;
}

3.1.4 string查找和替换
  • 替换: 查找指定字符串是否存在
  • 替换: 在指定的位置替换字符串

image-20250723102023803

代码示例:

#include <iostream>
#include <string>

using namespace std;


//字符串查找和替换

//1. 查找
void test01()
{
	string str1 = "decdefg";
	//find	从右往左
	int pos = str1.find("de");	//有返回下标 没有返回-1	第一次出现
	if (pos == -1)
		cout << "未找到字符串" << endl;
	else
		cout << "pos = " << pos << endl;

	//rfind 从左往右 
	pos = str1.rfind("de");	//有返回下标 没有返回-1	最后一次出现
	if (pos == -1)
		cout << "未找到字符串" << endl;
	else
		cout << "pos = " << pos << endl;
}


//2. 替换
void test02()
{
	string str1 = "abcdefg";

	//从1号位置起,3个字符替换为"1111"
	str1.replace(1, 3,"1111");
	cout << "str1 = " << str1 << endl;

}

int main()
{

	//test01();
	test02();
	return 0;
}

3.1.5 string字符串比较
  • 字符串比较是按照字符的ASCII码进行比较
  • 等于返回0
  • 大于返回1
  • 小于返回-1

代码示例:

#include <iostream>
#include <string>

using namespace std;


void test01()
{
	string str1 = "b";
	string str2 = "abcde";

	if (str1.compare(str2) == 0)
	{
		cout << "str1 == str2" << endl;
	}
	else if (str1.compare(str2) > 0)
		cout << "str1 > str2 " << endl;
	else
		cout << "str1 < str2" << endl;



}


int main()
{
	test01();
	return 0;
}

3.1.6 string 字符存取

string 中单个字符存取方式有两种

  • char& operator[](int n); 通过[]的方式取字符
  • char& at(int n);通过at方式获取字符

代码示例:

#include <iostream>
#include <string>

using namespace std;

void test01()
{
	string str1 = "abcdefg";

	for (int i = 0; i < str1.size(); i++)
		cout << str1[i] << " ";

	cout << endl;


	for (int i = 0; i < str1.size(); i++)
		cout << str1.at(i) << " ";

	cout << endl;

	//修改单个字符
	str1[0] = 'x';
	str1.at(1) = 'x';
	cout << str1 << endl;

}

int main()
{
	test01();
	return 0;
}

3.1.7 string插入和删除

image-20250723143101859

代码示例:

#include <iostream>
#include <string>

using namespace std;


void test01()
{
	string str = "hello";

	//插入
	str.insert(1, "111");
	cout << "str = " << str << endl;


	//删除
	str.erase(1, 3);
	cout << "str = " << str << endl;


}

int main()
{
	test01();
	return 0;
}

3.1.8 string子串
  • string subter(int pos = 0, int n = npos)const; 返回由pos开始的n个字符组成的字符串。

代码示例:

#include <iostream>
#include <string>

using namespace std;

void test01()
{
	string str = "abcdef";
	string subStr = str.substr(1, 3);

	cout << "subStr =  " << subStr << endl;

}

//使用操作
void test02()
{
	string email = "xl@code.com";
	
	//从邮件的地址中获取用户的信息
	int pos = email.find('@');
	
	string userName = email.substr(0, pos);

	cout << "userName = " << userName << endl;
}

int main()
{
	//test01();
	test02();
	return 0;
}

3.2 vector 容器
  • vector 数据结构和数组十分相似,也成为单端数组
  • 不同的是:数据是静态的,vector是动态的

动态扩展,并不是在原有的空间后继续开辟新的空间,而是寻找更大的空间,将原有数据拷贝新空间,释放原空间。

  • vector容器的迭代器,是支持随机访问的迭代器。
3.3.1 vector 构造函数

image-20250724152151512

代码示例:

#include <iostream>
#include <vector>

using namespace std;


void myPrint(vector<int> v)
{
	for (int i = 0; i < v.size(); i++)
		cout << v[i] << " ";

	cout << endl;
}


void test01()
{
	//默认构造函数
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	myPrint(v1);

	//区间构造
	vector<int> v2(v1.begin(), v1.end());
	myPrint(v1);


	//n个elem
	vector<int> v3(10, 6);
	myPrint(v3);


	//拷贝构造
	vector<int> v4(v3);
	myPrint(v4);

}


int main()
{
	test01();
	return 0;
}

3.3.2 vector 赋值操作

image-20250724153735072

代码示例:

#include <iostream>
#include <vector>

using namespace std;


void printVector(vector<int>& v)
{

	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";

	cout << endl;
}


void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	printVector(v1);

	//operator=
	vector<int> v2;
	v2 = v1;
	printVector(v2);

	//assign 区间赋值
	vector<int> v3;
	v3.assign(v2.begin(), v2.end());
	printVector(v3);

	//assign n个elem
	vector<int> v4;
	v4.assign(10, 6);
	printVector(v4);


}

int main()
{

	test01();
	return 0;
}

3.3.3 vector容量和大小

image-20250724155017378

代码示例:

#include <iostream>
#include <vector>

using namespace std;


void printVector(vector<int>& v1)
{
	for (int i = 0; i < v1.size(); i++)
		cout << v1[i] << " ";

	cout << endl;
}



void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);
	printVector(v1);

	if (v1.empty())
	{
		cout << "v1 为空! " << endl;
	}
	else
	{
		cout << "v1 的容量是: " << v1.capacity() << endl;
		cout << "v1 的大小是: " << v1.size() << endl;
	}

	v1.resize(15);	//默认新增的为0
	printVector(v1);
	v1.resize(20, 666);	//指定新增的为 666
	printVector(v1);

	v1.resize(5);
	printVector(v1);


}


int main()
{
	test01();
	return 0;
}

3.3.4 vector插入和删除

image-20250724160733658

代码示例:

#include <iostream>
#include <vector>

using namespace std;


void printVector(vector<int>& v)
{

	for (int i = 0; i < v.size(); i++)
		cout << v[i] << " ";

	cout << endl;
}


void test01()
{
	vector<int> v1;

	for (int i = 1; i <= 10; i++)
		v1.push_back(i);

	printVector(v1);

	for (int i = 0; i < 5; i++)
		v1.pop_back();
	
	printVector(v1);


	v1.insert(v1.begin(), 10);
	printVector(v1);

	v1.insert(v1.begin(), 3, 100);
	printVector(v1);

	v1.erase(v1.begin());
	printVector(v1);


	v1.erase(v1.begin(), v1.begin() + 3);
	printVector(v1);

	v1.clear();
	printVector(v1);

}

int main()
{
	test01();
	return 0;
}

3.3.5 vector数据存取

image-20250724164516903

#include <iostream>
#include <vector>

using namespace std;

void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	for (int i = 0; i < v1.size(); i++)
		cout << v1[i] << " ";

	cout << endl;

	for (int i = 0; i < v1.size(); i++)
		cout << v1.at(i) << " ";

	cout << endl;

	cout << "v1 中第一个元素是: " << v1.front() << endl;
	cout << "v1 中最后一个元素是: " << v1.back() << endl;

}


int main()
{

	test01();
	return 0;
}

3.3.6 vector互换容器
  • 实现两个容器内元素进行互换
  • swqp(vec); //将vect与本身的元素互换
#include <iostream>
#include <vector>

using namespace std;



void printVector(vector<int> v)
{
	for (int i = 0; i < v.size(); i++)
		cout << v[i] << " ";

	cout << endl;
}


//1.基本使用
void test01()
{
	vector<int> v1;
	cout << "交换前: " << endl;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	printVector(v1);

	vector<int> v2;
	for (int i = 10; i > 0; i--)
		v2.push_back(i);

	printVector(v2);

	cout << "交换后: " << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);



} 

//2.实际用途
//巧用swap可以收缩内存空间
void test02()
{
	vector<int> v;
	for (int i = 0; i < 100000; i++)
		v.push_back(i);

	cout << "v的容量是: " << v.capacity() << endl;
	cout << "v的大小是: " << v.size() << endl;
	cout << endl;


	v.resize(3);
	cout << "v的容量是: " << v.capacity() << endl;
	cout << "v的大小是: " << v.size() << endl;
	cout << endl;


	vector<int>(v).swap(v);	//将匿名vector和v互换,然后系统自动释放匿名。
	cout << "v的容量是: " << v.capacity() << endl;
	cout << "v的大小是: " << v.size() << endl;
	cout << endl;

}

int main()
{

	//test01();
	test02();
	return 0;
}

3.3.7 vector预留空间
  • 可以减少vector在动态扩展容量时的扩展次数
  • reserve(int len) //容器预留len个元素长度,预留位置不初始化,元素不可访问。

提前开辟可以减少中途新开辟空间,并迁移数据的麻烦。

image-20250724174720070

#include <iostream>
#include <vector>

using namespace std;

void test01()
{
	vector<int> v(1000000);
	int cnt = 0;
	int* p = NULL;

	for (int i = 0; i < 100000; i++)
	{
		/*if (v.size() >= v.capacity())
			cnt++;
		*/
		v.push_back(i);
		//看看开辟了多少次,迁移了多少次
		if (p != &v[0])
		{
			p = &v[0];
			cnt++;
		}

	}


	cout << "cnt = " << cnt;
}


int main()
{
	test01();
	return 0;
}

3.3 deque容器
  • 双端数组,可以对头端进行插入和删除操作。
  • deque相对而言,对头部的插入和删除速度比vector快
  • vector访问元素时速度比deque快,这和两者内部实现有关。

image-20250725083901808

3.3.1 deque构造函数

image-20250725084635824

代码示例:

#include <iostream>
#include <deque>


using namespace std;


void printDeque(const deque<int> d)
{
	for (auto it = d.begin(); it != d.end(); it++)
		cout << *it << " ";

	cout << endl;
}



void test01()
{
	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, 66);
	printDeque(d3);

	deque<int> d4(d3);
	printDeque(d4);

}


int main()
{
	test01();
	return 0;
}
  • deque 和 vector 容器的构造方式几乎一致,灵活使用即可。

3.3.2 deque赋值操作
  • deque的赋值操作和vector的赋值操作几乎一致。

image-20250725085952821

代码示例:

#include <iostream>
#include <deque>

using namespace std;


void printDeque(const deque<int>& d)
{
	for (auto it = d.begin(); it != d.end(); it++)
		cout << *it << " ";

	cout << endl;
}

void test01()
{
	deque<int> d1;
	for (int i = 0; i < 10; i++)
		d1.push_back(i);

	printDeque(d1);

	deque<int> d2;
	d2 = d1;
	printDeque(d2);

	deque<int> d3;
	d3.assign(d2.begin(), d2.end());
	printDeque(d3);

	deque<int> d4;
	d4.assign(10, 6);
	printDeque(d4);



}

int main()
{
	test01();
	return 0;
}
3.3.3 deque大小操作
  • 和vector的操作函数区别就少了一个容量,因为deque不需要判断容量,它本身就没有容量限制,可以无限的往后或者往前开辟空间。

image-20250725091442124


3.3.4deque插入和删除

image-20250725093738101

代码示例:

#include <iostream>
#include <deque>

using namespace std;


void printDeque(const deque<int>& d)
{
	for (auto it = d.begin(); it != d.end(); it++)
		cout << *it << " ";

	cout << endl;
}

void test01()
{
	deque<int> d1;
	//尾插
	for (int i = 0; i < 10; i++)
		d1.push_back(i);

	printDeque(d1);

	//头插
	for (int i = 0; i < 10; i++)
		d1.push_front(i);

	printDeque(d1);

	//尾删
	d1.pop_back();
	d1.pop_back();
	d1.pop_back();
	printDeque(d1);


	//头删
	d1.pop_front();
	d1.pop_front();
	d1.pop_front();
	printDeque(d1);


}

void test02()
{
	deque<int> d1;
	d1.push_back(1);
	d1.push_back(2);
	d1.push_back(3);
	printDeque(d1);


	d1.insert(d1.begin(), 10);
	d1.insert(d1.begin(), 20);
	printDeque(d1);

	d1.insert(d1.begin(), 2, 100);
	printDeque(d1);

	// 按照区间进行插入。
	deque<int> d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);

	d1.insert(d1.begin(), d2.begin(), d2.end());
	printDeque(d1);
}

void test03()
{
	deque<int> d1;
	d1.push_back(1);
	d1.push_back(2);
	d1.push_back(3);
	d1.push_back(4);

	printDeque(d1);
	auto it = d1.begin();
	it++;

	d1.erase(it);
	printDeque(d1);


	//按照区间删除
	//d1.erase(d1.begin(), d1.end());
	d1.clear();
	printDeque(d1);

}

int main()
{
	//test01();
	//test02();
	test03();
	return 0;
}

3.3.5 deque数据存取

image-20250725102917791

代码示例:

#include <iostream>
#include <deque>

using namespace std;



void test01()
{
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(30);
	d.push_back(40);
	d.push_back(50);

	for (int i = 0; i < d.size(); i++)
		cout << d[i] << " ";

	cout << endl;

	for (int i = 0; i < d.size(); i++)
		cout << d.at(i) << " ";

	cout << endl;

	cout << d.front() << endl;
	cout << d.back() << endl;


}

int main()
{
	test01();
	return 0;
}

3.3.6 deque 排序

算法:

  • sort(iterator beg, iterator end) // 对beg和end区间元素进行排序。

代码示例:

#include <iostream>
#include <deque>
#include <algorithm>

using namespace std;


void printDeque(const deque<int>& d)
{
	for (int i = 0; i < d.size(); i++)
		cout << d[i] << " ";

	cout << endl;
}


void test01()
{
	deque<int> d;
	d.push_back(10);
	d.push_back(20);
	d.push_back(260);
	d.push_back(9);
	d.push_back(37);

	printDeque(d);


	//排序 默认是升序
	sort(d.begin(), d.end());

	printDeque(d);

}


int main()
{
	test01();
	return 0;
}

3.4 stack 容器
  • stack – 栈是一种给先进后出的一种数据结构,它是由一个出口。
  • 栈只有栈顶可以由外界访问,因此不支持遍历行为。

image-20250725155327387

代码示例:

#include <iostream>
#include <stack>

using namespace std;

void test01()
{
	stack<int> s;
	s.push(10);
	s.push(20);
	s.push(30);
	s.push(40);
	s.push(50);

	cout << "栈中大小为: " << s.size() << endl;

	while (!s.empty())
	{
		cout << s.top() << endl;
		s.pop();
	}

	cout << "栈中大小为: " << s.size() << endl;

}

int main()
{
	test01();
	return 0;
}

3.5 queue 容器
  • queue – 队列是一种先进先出的数据结构,有两个出口.
  • 只有对头和队尾可以由外界访问,因此不支持遍历。
#include <iostream>
#include <string>
#include <queue>

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()
{
	queue<Person> q;
	Person p1("xl", 18);
	Person p2("N9", 19);
	Person p3("DBQ", 20);
	Person p4("XXiao", 21);
	Person p5("baby", 22);

	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);
	q.push(p5);


	cout << "队列中的大小为:d " << q.size() << endl;
	while (!q.empty())
	{
		cout << "队首元素: " << "姓名: " << q.front().m_Name << " 年龄: " << q.front().m_Age << endl;
		cout << "队尾元素: " << "姓名: " << q.back().m_Name << " 年龄: " << q.back().m_Age << endl;

		q.pop();
	}

	cout << "队列中的大小为: " << q.size() << endl;
}

int main()
{
	test01();
	return 0;
}
3.6 list 容器

list 又称链表

  • 链表是一种物理存储单元上非连续的存储结构。
  • 链表是由一系列节点组成,节点又由数据域指针域组成的。
  • 链表对于增删的效率要比vector高,但是遍历不如vector。

STL中的链表是一个双向循环链表

3.6.1 list 构造函数

image-20250726091616627

代码示例:

#include <iostream>
#include <list>

using namespace std;

void printList(const list<int>& l1)
{
	for (auto it = l1.begin(); it != l1.end(); it++)
		cout << *it << " ";
		

	cout << endl;
}


void test01()
{
	list<int> l1;
	for (int i = 0; i < 10; i++)
		l1.push_back(i);

	printList(l1);

	list<int> l2(l1);
	printList(l2);


	list<int> l3(l2.begin(), l2.end());
	printList(l3);

	list<int> l4(10, 6);
	printList(l4);

}


int main()
{

	test01();
	return 0;
}

3.6.2 list 赋值和交换

image-20250726093120738

代码示例:

#include <iostream>
#include <list>

using namespace std;


void printList(const list<int>& l)
{
   for (auto it = l.begin(); it != l.end(); it++)
   	cout << *it << " ";

   cout << endl;
}

//赋值
void test01()
{
   list<int> l1;
   
   for (int i = 0; i < 10; i++)
   	l1.push_back(i);


   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(10, 6);
   printList(l4);

}

void test02()
{
   list<int> l1;
   for (int i = 0; i < 10; i++)
   	l1.push_back(i);

   list<int> l2;
   l2.assign(10, 6);
   cout << "交换前: " << endl;
   printList(l1);
   printList(l2);

   swap(l1, l2);
   cout << "交换后: " << endl;
   printList(l1);
   printList(l2);

}

int main()
{
   //test01();
   test02();
   return 0;
}

3.6.3 list 大小操作

image-20250726094647479

代码示例:

#include <iostream>
#include <list>

using namespace std;

void printList(const list<int>& l)
{
	for (auto it = l.begin(); it != l.end(); it++)
		cout << *it << " ";

	cout << endl;
}


void test01()
{
	list<int> l1;
	for (int i = 0; i < 10; i++)
		l1.push_back(i);

	printList(l1);
	if (l1.empty())
	{
		cout << "l1 为空 " << endl;
	}
	else
	{
		cout << "l1 不为空! " << endl;
		cout << "l1 大小为: " << l1.size() << endl;
	}

	// resize
	l1.resize(15);
	printList(l1);


	l1.resize(20, 66);
	printList(l1);

	l1.resize(5);
	printList(l1);

}

int main()
{
	test01();
	return 0;
}

3.6.4 list 插入和删除

image-20250726095820737

代码示例:

#include <iostream>
#include <list>

using namespace std;

void printList(const list<int>& l)
{
	for (auto it = l.begin(); it != l.end(); it++)
		cout << *it << " ";
	
	cout << endl;
}


void test01()
{
	list<int> l1;
	//尾插
	l1.push_back(10);
	l1.push_back(20);
	l1.push_back(30);
	//头插
	l1.push_front(100);
	l1.push_front(200);
	l1.push_front(300);

	printList(l1);

	//头尾删
	l1.pop_back();
	l1.pop_front();
	printList(l1);

	auto it = l1.begin();
	l1.insert(l1.end(), 66);
	l1.insert(it, 6, 66);
	printList(l1);

	//区间插入
	list<int> l2;
	for (int i = 0; i < 5; i++)
		l2.push_back(521);
	it = l1.begin();
	it++;
	l1.insert(it, l2.begin(), l2.end());
	printList(l1);

	//删除
	it = l1.begin();
	l1.erase(it);
	printList(l1);


	l1.remove(521);
	printList(l1);

	l1.clear();
	printList(l1);


}


int main()
{
	test01();
	return 0;
}

3.6.5 list 数据存取
  • front() //返回第一个元素
  • back() //返回最后一个元素

list不支持随机访问

代码示例:

#include <iostream>
#include <list>

using namespace std;

void test01()
{
	list<int> l1;
	l1.push_back(10);
	l1.push_back(20);
	l1.push_back(30);
	l1.push_back(40);


	cout << "第一个元素为 " << l1.front() << endl;
	cout << "最后一个元素为 " << l1.back() << endl;

	//不支持随机访问
	auto it = l1.begin();
	it++;
	it--;
	//it = it + 1;	//error   不支持随机访问

}


int main()
{
	test01();
	return 0;
}
3.3.6 list 反转和排序
  • 将容器元素反转以及排序。

函数原型:

  • reverse(); //反转链表
  • srot(); //链表排序

代码示例:

#include <iostream>
#include <list>
#include <algorithm>

using namespace std;


void printList(const list<int>& l)
{
	for (auto it = l.begin(); it != l.end(); it++)
		cout << *it << " ";

	cout << endl;
}

bool myCompare(int v1, int v2)
{
	//第一个数 大于 第二数 10 9 8 .。。。。 
	return v1 > v2;	//降序
}


void test01()
{
	list<int> l1;
	l1.push_back(50);
	l1.push_back(20);
	l1.push_back(40);
	l1.push_back(10);
	l1.push_back(30);

	cout << "反转前: " << endl;
	printList(l1);
	//反转
	l1.reverse();
	cout << "反转后: " << endl;
	printList(l1);


	//排序
	//sort(l1.begin(), l1.end());	//标准算法库中的sort只能对支持随机访问的数据结构进行排序,比如strig vector 等等.
	cout << "排序前: " << endl;
	printList(l1);

	l1.sort();
	cout << "排序后: " << endl;
	printList(l1);

	l1.sort(myCompare);	//降序
	printList(l1);

}

int main()
{
	test01();
	return 0;
}

3.7 set/ multiset 容器
  • set容器中所有元素再插入时自动排序
  • set/multiset 属于关联式容器 底层用红黑树实现
  • set不允许出现重复的元素,multiset 允许出现重复的元素。
3.7.1 set 构造和赋值

image-20250726154044831

代码示例:

#include <iostream>
#include <set>

using namespace std;



void printSet(const set<int>& s)
{
	for (auto it = s.begin(); it != s.end(); it++)
		cout << *it << " ";

	cout << endl;
}
void printMultiset(const multiset<int>& s)
{
	for (auto it = s.begin(); it != s.end(); it++)
		cout << *it << " ";

	cout << endl;
}

void test01()
{
	set<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(60);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(50);

	//set中不支持重复的元素,只能插入一次
	//自动排序
	printSet(s1);

	set<int> s2(s1);
	printSet(s2);

	set<int> s3;
	s3 = s2;
	printSet(s3);


}
void test02()
{
	multiset<int> s1;

	s1.insert(10);
	s1.insert(30);
	s1.insert(60);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(50);

	//multiset中支持重复的元素,
	//自动排序
	printMultiset(s1);

	multiset<int> s2(s1);
	printMultiset(s2);

	multiset<int> s3;
	s3 = s2;
	printMultiset(s3);


}

int main()
{
	//test01();
	test02();
	return 0;
}

3.7.2 set 大小和交换
  • size(); // 返回容器中元素的数目
  • empty(); // 判断容器是否为空
  • swap(); //交换两个集合容器
#include <iostream>
#include <set>

using namespace std;

void printSet(const set<int>& s1)
{
	for (auto it = s1.begin(); it != s1.end(); it++)
		cout << *it << " ";

	cout << endl;
}


void test01()
{
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(30);

	printSet(s1);

	if (s1.empty())
	{
		cout << "s1 为空!" << endl;
	}
	else
	{
		cout << "s1 不为空! " << endl;
		cout << "s1 大小为: " << s1.size() << endl;
	}

	set<int> s2;
	s2.insert(100);
	s2.insert(400);
	s2.insert(200);
	s2.insert(300);
	cout << "交换前: " << endl;
	printSet(s1);
	printSet(s2);

	cout << "交换后: " << endl;
	s1.swap(s2);
	printSet(s1);
	printSet(s2);

}

int main()
{

	test01();
	return 0;
}

3.7.3 set 插入和删除

image-20250726161347926

#include <iostream>
#include <set>

using namespace std;


void printSet(const set<int>& s1)
{
	for (auto it = s1.begin(); it != s1.end(); it++)
		cout << *it << " ";

	cout << endl;
}



void test01()
{
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(30);
	s1.insert(20);

	printSet(s1);

	s1.erase(s1.begin());
	printSet(s1);

	s1.erase(30);
	printSet(s1);

	//清空
	//s1.erase(s1.begin(), s1.end());
	s1.clear();
	printSet(s1);

}


int main()
{
	test01();
	return 0;
}

3.7.4 set 查找和统计
  • find(key); // 查找key是否存在,若存在返回改键的元素的迭代器;若不存在,返回set.end();
  • count(key); // 统计key的元素个数。

代码示例:

#include <iostream>
#include <set>

using namespace std;


void printSet(const set<int>& s1)
{
	for (auto it = s1.begin(); it != s1.end(); it++)
		cout << *it << " ";

	cout << endl;
}



void test01()
{
	set<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(30);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);

	auto pos = s1.find(20);

	if (pos == s1.end())
	{
		cout << "没找到!" << endl;
	}
	else
	{
		cout << "找到了!" << endl;
	}
	
	cout << s1.count(20)  << endl;
}

void test02()
{
	multiset<int> s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(30);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);
	s1.insert(20);

	cout << s1.count(20) << endl;

}

int main()
{
	//test01();
	test02();
	return 0;
}

3.7.5 set 和 multiset的区别

我感觉最大的区别就是 set检测重复的数据,multiset不检测重复的数据。

如下图转到定义会发现: insert的返回值, set比multiset多一个bool型,这个bool就是看是否插入成功的。若书不存在则插入成功,返回true,若数据存在,则插入失败,返回false。

image-20250726171308638


3.7.6 pair 对组创建
  • 成对出现的数据,利用对组可以返回两个数据。

image-20250726172016695

代码示例:

#include <iostream>
#include <string>

using namespace std;

void test01()
{	
	pair<string, int>p("xl", 18);
	cout << "姓名: " << p.first << " 年龄: " << p.second << endl;

	pair<string, int> p2 = make_pair("N9", 21);
	cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;

}

int main()
{
	test01();
	return 0;
}

3.7.7 set 容器排序
  • set默认是从小到大,学习改变set容器的排序规则
  • 利用仿函数可以改变排序规则。

示例:set 存放内置数据类型与set存放自定义数据类型

#include <iostream>
#include <set>

using namespace std;


class MyCompare
{
public:
	//重载一下() 注意要加const
	//不是 const 函数,意思是编译器认为这个函数可能修改类的成员变量,
	//而标准库是不允许在 const 对象上调用非 const 函数的,于是就报错了。
	bool operator()(int v1, int v2) const
	{
		return v1 > v2;
	}

};

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) const
	{
		return p1.m_Age > p2.m_Age;
	}
};



// 1. set 存放内置数据类型
void test01()
{
	set<int> s1;

	//乱序插入
	s1.insert(10);
	s1.insert(50);
	s1.insert(30);
	s1.insert(40);
	s1.insert(20);

	//默认升序排序
	for (auto it = s1.begin(); it != s1.end(); it++)
		cout << *it << " ";
	cout << endl;

	//利用仿函数,改为降序排序
	set<int, MyCompare> s2;
	s2.insert(10);
	s2.insert(50);
	s2.insert(30);
	s2.insert(40);
	s2.insert(20);

	for (auto it = s2.begin(); it != s2.end(); it++)
		cout << *it << " ";
	cout << endl;

}


// 2. set 存放自定义数据类型
void test02()
{
	set<Person,comparePerson> s1;

	Person p1("xl", 18);
	Person p2("N9", 21);
	Person p3("Ayom", 35);
	Person p4("DBQ", 11);
	Person p5("577", 25);


	s1.insert(p1);
	s1.insert(p2);
	s1.insert(p3);
	s1.insert(p4);
	s1.insert(p5);

	for (auto it = s1.begin(); it != s1.end(); it++)
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;



}
int main()
{
	//test01(); //1.set 存放内置数据类型
	test02();	//2.set 存放自定义数据类型
	return 0;
}

image-20250726182136998

上面这张图片是在重载()时候的注意事项。


3.8 map/ multimap 容器
  • map属于关联式容器 的底层也是红黑树。

  • map中所有的元素都是pari

  • pair中第一个元素为key(键值) 起到索引作用, 第二个元素为value(实值)

  • 所有元素会根key值自动排序

优点:

  • 可以根据key值快速找到value值。

map和multimap的区别

  • map不允许容器中有重复key值元素。
  • multimap允许容器中有重复key值元素。
3.8.1 map 构造和赋值
  • map容器中插入是以pair形式插入的。

image-20250727150303741

代码示例:

#include <iostream>
#include <map>

using namespace std;

void printMap(map<int, int> m)
{
	for (auto it = m.begin(); it != m.end(); it++)
		cout << "key = " << it->first << " value = " << it->second << endl;

	cout << endl;
}

void test01()
{
	map<int, int> m;

	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(2, 20));

	printMap(m);

	map<int, int> m1(m);
	printMap(m1);

	map<int, int> m2;
	m2 = m1;

	printMap(m2);

}
int main()
{
	test01();
	return 0;
}

3.8.2 map 大小和交换
  • size(); // 返回容器中元素的数目
  • empty(); //判断容器是否为空
  • swap(st); //交换两个集合容器

代码示例:

#include <iostream>
#include <map>

using namespace std;

void printMap(map<int, int>& m)
{
	for (auto it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}

	cout << endl;
}

void test01()
{
	map<int, int> m1;
	m1.insert(pair<int, int>(1, 10));
	m1.insert(pair<int, int>(2, 20));
	m1.insert(pair<int, int>(3, 30));
	m1.insert(pair<int, int>(4, 40));

	if (m1.empty())
	{
		cout << "m1 为空!" << endl;
	}
	else
	{
		cout << "m1 不为空! " << endl;
		cout << "m1 的大小为: " << m1.size() << endl;
	}


	map<int, int> m2;
	m2.insert(pair<int, int>(10, 100));
	m2.insert(pair<int, int>(20, 200));
	m2.insert(pair<int, int>(30, 300));
	m2.insert(pair<int, int>(40, 400));

	cout << "交换前: " << endl;
	printMap(m1);
	printMap(m2);

	
	cout << "交换后: " << endl;
	m1.swap(m2);
	printMap(m1);
	printMap(m2);
}

int main()
{
	test01();
	return 0;
}

3.8.3 map 插入和删除

image-20250727153758875

代码示例:

#include <iostream>
#include <map>

using namespace std;

void printMap(map<int, int>& m)
{
	for (auto it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}

	cout << endl;
}

void test01()
{
	map<int, int> m1;

	//插入 四 种方式
	m1.insert(pair<int, int>(1, 10));

	m1.insert(make_pair(2, 20));

	m1.insert(map<int, int>::value_type(3, 30));

	m1[4] = 40;

	cout << m1[5] << endl;	//出现m1[5]自动就在map中插入了key为5 value 为0的值。
	printMap(m1);
	
	//删除
	m1.erase(m1.begin());
	printMap(m1);

	m1.erase(5);	//按照key删除
	printMap(m1);

	//清空
	//m1.erase(m1.begin(), m1.end());
	m1.clear();
	printMap(m1);
}
int main()
{
	test01();
	return 0;
}
3.8.4 map 查找和统计
  • find(); // 查找key是否存在,若存在返回元素的迭代器,否则返回set.end();
  • cout(); // 统计key的元素个数
#include <iostream>
#include <map>

using namespace std;


void test01()
{
	map<int, int> m1;
	m1[1] = 10;
	m1[2] = 20;
	m1[3] = 30;
	m1[4] = 40;
	m1.insert(make_pair(3, 30));
	m1.insert(make_pair(3, 30));
	m1.insert(make_pair(3, 30));
	m1.insert(make_pair(3, 30));

	auto pos = m1.find(4);

	if (pos != m1.end())
	{
		cout << "找到了!" << endl;
		cout << "key = " << pos->first << " value = " << pos->second << endl;
	}
	else
		cout << "没找到!" << endl;
	
	cout << m1.count(3) << endl;

	multimap<int, int> m2;
	m2.insert(make_pair(3, 30));
	m2.insert(make_pair(3, 30));
	m2.insert(make_pair(3, 30));
	m2.insert(make_pair(3, 30));
	m2.insert(make_pair(3, 30));


	cout << m2.count(3) << endl;


}
int main()
{
	test01();
	return 0;
}

3.8.5 map 排序

也是默认从小到大排序,和set的方式几乎一样。

我们下面将其改为降序排序

#include <iostream>
#include <map>

using namespace std;


class MyCompare
{
public:
	bool operator()(int v1, int v2) const
	{
		return v1 > v2;
	}
};


void test01()
{
	map<int, int, MyCompare> m1;

	m1.insert(make_pair(1, 10));
	m1.insert(make_pair(3, 30));
	m1.insert(make_pair(2, 20));
	m1.insert(make_pair(5, 50));
	m1.insert(make_pair(4, 40));

	for (auto it = m1.begin(); it != m1.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}


}

int main()
{

	test01();
	return 0;
}

4. STL- 函数对象

4.1 函数对象

4.1.1 函数对象概念

概念:

  • 重载函数调用操作符的类,其对象常称为函数对象。
  • 函数对象使用重载的()时候,行为类似函数调用,也叫仿函数

本质:

  • 函数对象(仿函数)是一个类,不是一个函数。
4.1.2 函数对象的使用
  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态。
  • 函数对象可以作为参数传递

代码示例:

#include <iostream>
#include <string>

using namespace std;

/*
- 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
- 函数对象超出普通函数的概念,函数对象可以有自己的状态。
- 函数对象可以作为参数传递
*/

//1. 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值。
class MyAdd
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}
};
void test01()
{
	MyAdd myAdd;
	cout << myAdd(1, 1) << endl;
}


//2. 函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:
	MyPrint()
	{
		count = 0;
	}

	void operator()(string test)
	{
		cout << test << endl;
		this->count++;
	}
	
	int count;
};

void test02()
{
	MyPrint myPrint;

	myPrint("hello N9!");
	myPrint("hello N9!");
	myPrint("hello N9!");
	myPrint("hello N9!");
	myPrint("hello N9!");

	cout << "myPrint 调用次数: " << myPrint.count << endl;
}

//3. 函数对象可以作为参数传递
void doPrint(MyPrint & mp, string test)
{
	mp(test);
}

void test03()
{
	MyPrint myPrint;
	doPrint(myPrint, "hello DBQ");
}

int main()
{
	test01();
	test02();
	test03();
	return 0;
}

4.2 谓词

概念:

  • 返回bool类型的仿函数称为谓词
  • 如果operatro()接受一个参数,那么称为一元谓词
  • 如果operator()接受两个参数,那么称为二元谓词
4.2.1 一元谓词

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class CreateFive
{
public:
	//- 返回bool类型的仿函数称为谓词
	//-如果operatro()接受一个参数,那么称为一元谓词
	bool operator()(int val)
	{
		return val > 5;
	}

};


void test01()
{
	vector<int> v;
	
	for (int i = 0; i < 10; i++)
		v.push_back(i);

	auto it = find_if(v.begin(), v.end(), CreateFive());
	if (it == v.end())
	{
		cout << "没有找到! " << endl;
	}
	else
	{
		cout << "找到了 " << *it << endl;
	}

}

int main()
{
	test01();
	return 0;
}
4.2.2 二元谓词

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


//bool Compare(int v1, int v2)
//{
//	return v1 > v2;
//}

class ClassCompare
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void test01()
{
	vector<int> v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(50);
	v.push_back(30);
	v.push_back(20);

	sort(v.begin(), v.end());

	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";
	cout << endl;

	
	//sort(v.begin(), v.end(), Compare);

	sort(v.begin(), v.end(), ClassCompare());
	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";
	cout << endl;

}

int main()
{
	test01();
	return 0;
}

4.3 内建函数对象

4.3.1 内建函数对象意义
  • STL提供了一些函数对象
  • 算术仿函数
  • 关系仿函数
  • 逻辑仿函数

这些仿函数所产生的对象,用法和一般函数完全相同,但是使用时候,需要引入头文件#include<functional>

4.3.2 算数仿函数

image-20250728161941759

#include <iostream>
#include <functional>
using namespace std;

// 一元仿函数. 取反
void test01()
{
	negate<int> n;

	cout << n(10) << endl;
	cout << n(-20) << endl;
	cout << n(666) << endl;

}

// 二元仿函数 加法
void test02()
{
	plus<int> p;

	cout << p(1, 1) << endl;
	cout << p(1, 2) << endl;
	cout << p(1, 3) << endl;

}

int main()
{
	test01();
	test02();
	return 0;
}

4.3.3 关系仿函数

image-20250728163300622

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>

using namespace std;

class MyCompare
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};


//大于 greater
void test01()
{
	vector<int> v;

	v.push_back(10);
	v.push_back(50);
	v.push_back(30);
	v.push_back(40);
	v.push_back(20);


	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";

	cout << endl;

	//相同
	//sort(v.begin(), v.end(), MyCompare());
	sort(v.begin(), v.end(), greater<int>());

	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";

}


int main()
{
	test01();
	return 0;
}
4.3.4 逻辑仿函数

image-20250728164401796

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>


using namespace std;

//逻辑非 logical_not
void test01()
{
	vector<bool> v;
	v.push_back(true);
	v.push_back(false);
	v.push_back(true);
	v.push_back(false);

	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";

	cout << endl;;

	//利用逻辑非 将容器v搬运到v2中, 并执行取反操作
	vector<bool> v2;

	//必须提前开辟空间
	v2.resize(v.size());

	transform(v.begin(), v.end(),v2.begin(), logical_not<bool>());

	for (auto it = v2.begin(); it != v2.end(); it++)
		cout << *it << " ";
	
}


int main()
{
	test01();
	return 0;
}
  • 逻辑仿函数实际应用很少,了解即可.

5. STL - 常用算法

  • 算法主要由头文件 <algorithm><functional><numeric> 组成
  • <algorithm> 是所有STL头文件中最大的一个,范围涉及到比较,交换,查找,遍历,复制,修改等等。
  • <numeric> 体积小,只包括几个序列上面进行简单数学运算的模板算法。
  • <functional> 定义了一些模板类,用以声明函数对象

5.1 常用的遍历算法

掌握常用的遍历算法

  • for_each 遍历容器
  • transform 搬运容器到另一个容器中
5.1.1 for_each
  • for_each(iterator beg, iterator end, _func);

  • beg开始迭代器,end结束迭代器,_func函数或者函数对象

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


//普通函数
void print01(int val)
{
	cout << val << " ";
}

void test01()
{
	vector<int>  v;

	for (int i = 0; i < 10; i++)
		v.push_back(i);

	for_each(v.begin(), v.end(), print01);
	cout << endl;

}

// 函数对象(仿函数)
class print02
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test02()
{
	vector<int>  v;

	for (int i = 0; i < 10; i++)
		v.push_back(i);

	for_each(v.begin(), v.end(), print02());
	cout << endl;


}

int main()
{
	test01();

	test01();
	return 0;
}
5.1.2 transform
  • transform(iterator beg1, iterator end1, iterator beg2, _func);
  • beg1 源容器开始迭代器, end1源容器结束迭代器,beg2 目标容器开始迭代器, _func 函数或者函数对象。
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;


class TransForm
{
public:
	int operator()(int val)
	{
		//支持操作
		return val%2;
	}
	
};

class MyPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};


void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	vector<int> v2;
	//提前开辟空间
	v2.resize(v1.size());

	transform(v1.begin(), v1.end(), v2.begin(), TransForm());
	for_each(v2.begin(), v2.end(), MyPrint());

}

int main()
{
	test01();
	return 0;
}
  • 目标容器必须体检开辟空间,否则无法正常搬运。

5.2 常用的查找算法

5.2.1 find
  • find(iterator beg, iterator end, value)

按值来查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

//1. 查找内置数据类型
void test01()
{
	vector<int> v;

	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);
	v.push_back(50);


	auto it = find(v.begin(), v.end(), 20);

	if (it == v.end())
	{
		cout << "没有找到。" << endl;
	}
	else
	{
		cout << "找到了!" << endl;
	}


}

//2. 查找自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator==(const Person& p)
	{
		return this->m_Name == p.m_Name && this->m_Age == p.m_Age;
	}
	string m_Name;
	int m_Age;
};
void test02()
{
	vector<Person> v;

	Person p1("aaa", 11);
	Person p2("bbb", 12);
	Person p3("ccc", 13);
	Person p4("ddd", 14);
	Person p5("eee", 15);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	Person pp("ccc", 12);

	auto it = find(v.begin(), v.end(), pp);
	if (it == v.end())
	{
		cout << "没有找到。" << endl;
	}
	else
	{
		cout << "找到了!" << endl;
	}

}
int main()
{

	//test01();
	test02();
	return 0;
}
5.2.2 find_if
  • 按照条件查找元素
  • find_if(iterator beg, iterator end, _Pred);

按照 _Pred 的条件来找元素,找到返回迭代器。

_Pred 是一个函数或者谓词(返回bool类型的仿函数)

代码示例:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;




//1. 查找内置数据类型
// 函数方式
//bool CreateFive(int val)
//{
//	return val > 5;
//}

//谓词方式
class CreateFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};

void test01()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}

	auto it = find_if(v.begin(), v.end(), CreateFive());
	if (it == v.end())
	{
		cout << "找不到" << endl;
	}
	else
	{
		cout << "找到了大于5的数为: " << *it << endl;
	}
	
}


//2. 查找自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}


	string m_Name;
	int m_Age;
};


class Greater20
{
public:
	bool operator()(const Person& p)
	{
		return p.m_Age > 20;
	}
};

void test02()
{
	vector<Person> v;
	Person p1("aaa", 10);
	Person p2("bbb", 21);
	Person p3("ccc", 15);
	Person p4("ddd", 40);


	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	auto it = find_if(v.begin(), v.end(), Greater20());
	if (it == v.end())
	{
		cout << "没找到!" << endl;
	}
	else
	{
		cout << "找到了 姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}

int main()
{
	//test01();
	test02();
	return 0;
}
5.2.3 adjacent_find
  • 查找相邻重复元素
  • adjacent_find(iterator beg, iterator end)

查找相邻重复元素,返回相邻元素的第一个位置的迭代器

代码示例:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(5);

	auto it = adjacent_find(v.begin(), v.end());
	if (it == v.end())
	{
		cout << "没有找到! " << endl;
	}
	else
	{
		cout << "找到了他是:" << *it << endl;
	}
}

int main()
{
	test01();
	return 0;
}
5.2.4 binary_search
  • 查找指定元素是否存在
  • bool binary_search(iterator beg, iterator end, value);

查找指定的元素,查到返回true,查不到返回false。

注意必须在有序序列中查找。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
		v.push_back(i);

	//必须是有序的
	//无序序列结果未知。
	if (binary_search(v.begin(), v.end(), 8))
	{
		cout << "找到了 " << endl;
	}
	else
	{
		cout << "没有找到 " << endl;
	}

}

int main()
{

	test01();
	return 0;
}
5.2.5 count
  • 统计元素个数
  • count(iterator beg, iterator end, value);

统计在beg到end这个区间内出现的次数。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;


//1. 统计内置数据类型
void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(1);
	v.push_back(2);
	v.push_back(2);
	v.push_back(2);
	v.push_back(3);

	int ret = count(v.begin(), v.end(), 2);

	cout << ret << endl;

}

//2. 统计自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->n_Name = name;
		this->m_Age = age;
	}

	bool operator==(const Person& p)
	{
		return this->m_Age == p.m_Age;
	}

	string n_Name;
	int m_Age;
};
void test02()
{
	vector<Person> v;

	Person p1("xl", 21);
	Person p2("N9", 21);
	Person p3("DBQ", 21);
	Person p4("Ayom", 25);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);

	Person p5("577", 21);
	int ret = count(v.begin(), v.end(), p5);

	cout << "和" << p5.n_Name << "同岁的有: " << ret << " 个" << endl;

}


int main()
{
	//test01();
	test02();
	return 0;
}
5.2.6 count_if
  • 按条件统计元素个数
  • count_if(iterator beg, iterator end, _Pred);

从beg到end这个区间里面按照谓词_Pred里面的条件统计元素的个数。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

//1. 内置数据类型
class Greater2
{
public:
	bool operator()(int v)
	{
		return v > 2;
	}

};

void test01()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);

	int ret = count_if(v.begin(), v.end(), Greater2());
	cout << ret << endl;
}

//2. 自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};
class Greater18
{
public:
	bool operator()(const Person& p)
	{
		return p.m_Age > 18;
	}
};

void test02()
{
	vector<Person> v;
	Person p1("aaa", 13);
	Person p2("bbb", 18);
	Person p3("ccc", 22);
	Person p4("ddd", 25);
	Person p5("ddd", 31);
	Person p6("ddd", 100);


	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	v.push_back(p6);


	int ret = count_if(v.begin(), v.end(), Greater18());
	cout << ret << endl;
}

int main()
{
	//test01();
	test02();
	return 0;
}

5.3 常用的排序算法

5.3.1 sort
  • 给容器排序

  • sort(iterator beg, iterator end, _Pred);

将区间beg到end排序,默认从小到大,_Pred谓词可以改变排序顺序。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

void Myprint(int val)
{
	cout << val << " ";
}

void test01()
{
	vector<int> v;
	v.push_back(2);
	v.push_back(5);
	v.push_back(1);
	v.push_back(3);
	v.push_back(4);

	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), Myprint);
	cout << endl;
	//降序
	sort(v.begin(), v.end(), greater<int>());
	for_each(v.begin(), v.end(), Myprint);

}


int main()
{
	test01();
	return 0; 
}
5.3.2 random_shuffle
  • 洗牌 指定范围内的元素随机调整次序
  • random_shuffle(iterator beg, iterator end);
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>

using namespace std;

void test01()
{
	srand((unsigned)time(NULL));
	vector<int> v;
	for (int i = 0; i < 10; i++)
		v.push_back(i);

	random_shuffle(v.begin(), v.end());

	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";
	cout << endl;

}

int main()
{
	test01();
	return 0;
}

用的时候记得加随机数种子

5.3.3 merge
  • 将两个容器合并,并存储到另一个容器中
  • merge(iterator beg1, end1, beg2, end2, dest);

两个有序的容器合并到dest目标容器。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v1;
	vector<int> v2;
	//有序序列
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i+1);
	}

	//必须有足够的空间
	vector<int> v3;
	v3.resize(v1.size() + v2.size());

	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());

	for (auto it = v3.begin(); it != v3.end(); it++)
		cout << *it << " ";

	cout << endl;

}

int main()
{
	test01();
	return 0;
}
5.3.4 reverse
  • 将容器内元素进行反转
  • reverse(iterator beg, iterator end);

反转beg到end区间内元素。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}

	reverse(v1.begin(), v1.end());

	for (auto it = v1.begin(); it != v1.end(); it++)
		cout << *it << " ";

	cout << endl;

}

int main()
{
	test01();
	return 0;
}

5.4 常用的拷贝和替换算法

5.4.1 copy
  • 拷贝函数
  • copy(iterator beg, iterator end, iterator dest);

将beg到end区间的元素全部拷贝到dest目标容器中,dest是目标起始迭代器。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v1;
	for (int i = 0; i < 10; i++)
		v1.push_back(i);

	vector<int> v2;
	v2.resize(v1.size());
	copy(v1.begin(), v1.end(), v2.begin());

	for (auto it = v2.begin(); it != v2.end(); it++)
		cout << *it << " ";

	cout << endl;

}

int main()
{
	test01();
	return 0;
}
5.4.2 replace
  • 将容器内指定范围的旧元素修改为新元素。
  • replace(iterator beg, iterator end, oldvalue, newvalue);

将区间beg到end中的oldvalue改为newvalue

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void test01()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(1);

	//将1 改为 10
	//
	replace(v1.begin(), v1.end(), 1, 10);

	for (auto it = v1.begin(); it != v1.end(); it++)
		cout << *it << " ";

	cout << endl;

}

int main()
{
	test01();
	return 0;
}
5.4.3 replace_if
  • 将区间内满足条件的元素,替换成指定元素。
  • replace_if(iterator beg, iterator end, _Pred, newvalue);

将去区间内,满足_Pred条件的全部替换为newvalue.

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;


class Greater2
{
public:
	bool operator()(int val)
	{
		return val > 2;
	}

};

//1. 内置数据类型
void test01()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);
	for (auto it = v1.begin(); it != v1.end(); it++)
		cout << *it << " ";
	cout << endl;

	replace_if(v1.begin(), v1.end(), Greater2(), 2);

	for (auto it = v1.begin(); it != v1.end(); it++)
		cout << *it << " ";
	cout << endl;
}

//2. 自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};
class Greater18
{
public:
	bool operator()(const Person& p)
	{
		return p.m_Age > 18;
	}
};


void test02()
{
	vector<Person> v;
	Person p1("aaa", 18);
	Person p2("aaa", 21);
	Person p3("aaa", 30);
	Person p4("aaa", 12);
	Person p5("aaa", 13);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	for (auto it = v.begin(); it != v.end(); it++)
		cout << "姓名:" << it->m_Name << " 年龄: " << it->m_Age;
	cout << endl;



	Person pp("ccc", 18);
	replace_if(v.begin(), v.end(), Greater18(), pp);

	for (auto it = v.begin(); it != v.end(); it++)
		cout << "姓名:" << it->m_Name << " 年龄: " << it->m_Age;
	cout << endl;


}

int main()
{
	//test01();
	test02();
	return 0;
}
5.4.4 swap
  • 互换两个相同容器的元素
  • swap(container c1, container c2)

不仅可以互换容器,还可以互换以下。

类型 能否使用 swap 备注
基本类型(int, double 等) 最基本用法
STL容器 如 vector、map、set 等
原始指针 int* p1, *p2; std::swap(p1, p2);
自定义类型 成员可交换,或你自定义了 swap
智能指针(shared_ptr, unique_ptr 内部实现了 swap
数组(如 int a[10], int b[10] ❌(需要手动交换元素) 原生数组不支持整体 swap
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void myPrint(int val)
{
	cout << val << " ";
}

void test01()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 10);
	}

	cout << "交换前: " << endl;
	for_each(v1.begin(), v1.end(), myPrint);
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint);
	cout << endl;


	cout << "--------------------------------------" << endl;
	cout << "交换后: " << endl;
	swap(v1, v2);

	for_each(v1.begin(), v1.end(), myPrint);
	cout << endl;
	for_each(v2.begin(), v2.end(), myPrint);
	cout << endl;


}

int main()
{
	test01();
	return 0;
}

5.5 常用的算术生成算法

头文件<numeric>

5.5.1 accumulate
  • 计算区间内容器元素积累总和
  • accumlate(iterator beg, iterator end, value);

将区间内的元素和加起来,value是起始累加值

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;


void test01()
{
	vector<int> v;
	for (int i = 0; i <= 100; i++)
		v.push_back(i);

	int total = accumulate(v.begin(), v.end(), 1000); // + 1000

	cout << total << endl;
}

int main()
{
	test01();
	return 0;
}
5.5.2 fill
  • 向函数中填充指定的元素
  • fill(iterator beg, iterator end, value);

将区间内的元素填充成value

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;


void test01()
{
	vector<int> v;
	v.resize(10);

	//后期填充
	fill(v.begin(), v.end(), 10);
	for (auto it = v.begin(); it != v.end(); it++)
		cout << *it << " ";
	cout << endl;
}

int main()
{
	test01();
	return 0;
}

5.6 常用的集合算法

掌握交集,并集,差集。

5.6.1 set_intersection
  • 交集,两个容器共同出现的元素。

  • set_intersection(beg1,end1,beg2,end2,dest)

将两个容器的交集置于目标容器中去,dest为目标容器的起始迭代器, 返回交集最后一个迭代器。

#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;


void test01()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 5); // 5~14
	}

	vector<int> v3;
	//最特殊的情况就是一个容器包含另一个容器
	v3.resize(min(v1.size(), v2.size()));
	auto itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());

	for (auto it = v3.begin(); it != itEnd; it++)
		cout << *it << " ";
	cout << endl;

}

int main()
{
	test01();
	return 0;
}
5.6.2 set_union
  • 并集,两个有序容器合并。
  • set_union(beg1,end1,beg2,end2,dest)

将两个容器取并集,必须是有序容器,然后返回并集后的最后一个迭代器。

#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;


void test01()
{
   vector<int> v1, v2;
   for (int i = 0; i < 10; i++)
   {
   	v1.push_back(i);
   	v2.push_back(i + 5); // 5~14
   }

   vector<int> v3;
   //最特殊的情况就是两个容器元素全部不一样,需要全部合并。
   v3.resize(v1.size() + v2.size());
   auto itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());

   for (auto it = v3.begin(); it != itEnd; it++)
   	cout << *it << " ";
   cout << endl;

}

int main()
{
   test01();
   return 0;
}
5.6.3 set_difference
  • 差集:属于集合 A 但不属于集合 B 的元素
  • set_union(beg1,end1,beg2,end2,dest)

将两个容器的差集,放入目标容器中去,但要注意谁与谁的差集。

image-20250731200746503

#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;


void test01()
{
	vector<int> v1, v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 5); // 5~14
	}

	vector<int> v3;
	//最特殊的情况就是两个容器没有交集,取最大的容器即可
	v3.resize(max(v1.size(), v2.size()));
	

	cout << "v1 和 v2 容器的差集: " << endl;
	auto itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v3.begin());
	for (auto it = v3.begin(); it != itEnd; it++)
		cout << *it << " ";
	cout << endl;



	cout << "v2 和 v1 容器的差集: " << endl;
	itEnd = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v3.begin());
	for (auto it = v3.begin(); it != itEnd; it++)
		cout << *it << " ";
	cout << endl;

}

int main()
{
	test01();
	return 0;
}


课程结束🎉🌸,完美收官!👏🌟