C++刷题--STL 学习笔记(持续更新整合)

从C到c++刷题,补充大概内容

c++头文件、编译、命名空间、c++的输入输出、new/delete、STL。

编译

c++能够完全支持c,打开一个c++文件,用c语言的知识写,完全能够运行(关键字不冲突情况下)。

C++头文件

万能头文件

#include // 万能头文件,STL容器所用头文件可以直接用这个,不用挨个记

原C语言头文件:

C语言头文件就是去掉 .h 然后前面加上 c,变成 c++头文件。

#include  // 相当于C语⾔⾥⾯的#include 
#include  // 相当于C语⾔⾥⾯的#include 
#include  // 相当于C语⾔⾥⾯的#include 
#include  // 相当于C语⾔⾥⾯的#include 

命名空间-using namespace std

        刷题时候都是为了方便,所以才使用 using namespace std; 这样想使用c++标准库std中的一些函数直接用就行了。

        如 cout << "hello";

        如果不这样引入,为了避免你自己写的代码中自定义的变量或者函数名与标准库中的冲突,c++要求,你能使用std中的函数时,要带上作用域 std::,这样就说清楚你用的函数是标准库的还是你自定义的。为了提高写代码效率,我们一般在cpp的开头加上 using namespace std,用以说明代码中出现的类似cout等标准库中的函数名就是指的标准库中的函数。

  更具体理解,可参考如下:

【命名空间】using namespace std是什么意思?_c语言using namespace std_罅隙`的博客-CSDN博客

一文弄清using namespace std;的作用[2021最新版]_usingnamespacestd的作用-CSDN博客

输入输出

c++输入输出的时候,不用printf、scanf,使用cin、cout,不用考虑繁杂的数据类型。

C语言常见输入函数(scanf(),getchar(),getche(),getch(),gets())大汇总_c语言的输入函数_夏微凉.的博客-CSDN博客

#include 
#include 
using namespace std;
int main()
{
	int a,b;
	cin>>a>>b;//输入,就是这个格式 cin和>>
	cout<>c[i];
 	}
 	for(int i=1;i<=10;i++)
 	{
 		cout<

标准输入流(cin

cin全名叫 标准输入流。和C语言中的scanf用法类似,只是它用起来更为方便(大多数情况下),不需要区分不同的类型用不同的写法,所有类型只需要用cin >> (变量名)就可以了。

cout标准输出流,与C中的printf类似,但是它也不用区分不同的类型用不同输出符号,不管什么类型只要用cout << (变量名) 就可以了。其中endl相当于C语言中的 ‘\n’ 换行符。

流插入运算符 << 在一个语句中可以多次使用,如上面实例中所示,endl 用于在行末添加一个换行符。

cout是向左(开口向右)的两个箭头,注意和cin区分开来。

cin 和 cout ⽐较⽅便,不⽤像C语⾔⾥的scanf、printf那样写得那样繁琐,cin >> n;和scanf("%d", &n);⼀样(⽽且⽤cin再也不⽤担⼼像scanf⼀样忘记写取地址符&了),同样,cout << n;和printf("%d", n);⽽且不管n是double还是int或者是char类型,只⽤写cin >> n; 和cout << n;这样简单的语句就好,不⽤像C语⾔中需要根据n的类型对应地写%lf、%d、%c这样麻烦。

new/delete使用

语法:new 数据类型

  • new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。

  • delete p的时候:首先调用这个对象的析构函数,然后再释放这个对象本身的空间。

单元素

int* func()
{
	int* a = new int(10);
	return a;
}

int main() {

	int *p = func();

	cout << *p << endl;
	cout << *p << endl;

	//利用delete释放堆区数据
	delete p;

	//cout << *p << endl; //报错,释放的空间不可访问

	system("pause");

	return 0;
}
Car *Audi = new Car();

delete Audi;

一维内置 

char* p = new char[10];//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址

delete[] p;

二维内置

C++刷题--STL 学习笔记(持续更新整合)_第1张图片

int** pc = new int*[3];//这边表示开辟行数为3
//int*[3]表示的为开辟三个存放int*元素的数组,所以才有了下一步pc[i]中对列数的开辟
	for (int i = 0; i < 3; i++)
	{
		pc[i] = new int[2];//这边表示开辟列数为2
		for (int j = 0; j < 2; j++)
		{
			pc[i][j] = i + j;
			cout <

 三维内置

int ***array3D;
//假定数组第一维为m, 第二维为n, 第三维为h
//动态分配空间
array3D = new int **[m];
for( int i=0; i

new/delete 和malloc/free 的区别

  • 1 属性方面 new是关键字,需要编译器支持;malloc是库函数,需要头文件支持。
  • 2 参数方面 new申请内存无需指定内存大小,编译器会根据类型信息自行计算。除此之外,new会调用构造函数。
int* p=new int;//分配大小为sizeof(int)的空间
int* p=new int(6);//分配大小为sizeof(int)的空间,并且初始化为6

        malloc必须由我们计算需要申请的字节数,需要显式指出所需内存的尺寸,并且返回后强行转换为实际类型的指针。而且malloc只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值是随机的。

int* p=(int)malloc(sizeof(int)100);//分配可以放下100个int的内存空间
  • 3 处理数组方面

        new有处理数组的new[],使用new[]分配的内存必须使用delete[]进行释放。

int* ptr=new int[100];//分配100个int的内存空间
delete[] ptr;

        malloc要想动态分配一个数组的内存,需要我们手动定义数组的大小。使用malloc分配内存必须使用free来释放内存。

int* p=(int)malloc(sizeof(int)100);//分配可以放下100个int的内存空间
  • 4 返回类型 new分配成功返回的是对象类型指针,与对象严格匹配,无类型转换,所以new是符合类型安全性操作符;malloc返回值类型是void*,一般需要接强制类型转换成我们需要的类型。

  • 5 分配失败方面 new内存分配失败的时候,抛出bad_ alloc异常 ;malloc分配内存失败时返回NULL。

  • 6 自定义类型方面 new会先调用operator new函数,申请足够的内存,然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存。 malloc是库函数,只能动态地申请和释放内存,无法强制要求其做自定义类型对象构造和析构函数。

c++ STL的使用

常见STL容器总结_zhangyin_blog的博客-CSDN博客 (待参考)

STL简介

  1. STL(Standard Template Library,标准模板库) ,STL 几乎所有的代码都采用了模板类或者模板函数 。
  2. 大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作。为了建立数据结构和算法的一套标准,STL 充分运用C++的面向对象泛型编程思想,借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。极大提升c++数据结构和算法代码的复用性。
  3. 下面STL中的 vector 就是一个典型的模板类,其实例一个对象就是典型的模板的用法。通过尖括号 < >先确定模板的数据类型,然后后面是对象名和构造函数的入参。

新建一个向量存储int类型
vectortest; // 定义的时候不指定vector的⼤⼩
vectortest2(6); // 定义的时候指定vector的⼤⼩,默认test2⾥⾯元素都是0
vectortest3(6, 3); // 定义的时候指定vector的⼤⼩,默认test3⾥⾯元素都是3
vector test{1,2,3};//跟数组差不多
  1. vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。
  2. STL 最初由惠普实验室开发,于 1998 年被定为国际标准,正式成为 C++ 程序库的重要组成部分。值得一提的是,如今 STL 已完全被内置到支持 C++ 的编译器中,无需额外安装,这可能也是 STL 被广泛使用的原因之一。
  3. 从根本上说,STL 是一些容器、算法和其他一些组件的集合,所有容器和算法都是总结了几十年来算法和数据结构的研究成果,汇集了许多计算机专家学者经验的基础上实现的,因此可以说,STL 基本上达到了各种存储方法和相关算法的高度优化。注意,这里提到的容器,本质上就是封装有数据结构的模板类,例如 list、vector、set、map 等,有关这些容器的具体用法,后续章节会做详细介绍。

C++ STL有什么用?

C++刷题--STL 学习笔记(持续更新整合)_第2张图片​ 

vector  a; //定义 a 数组,当前数组长度为 0,但和普通数组不同的是,此数组 a 可以根据存储数据的数量自动变长。
//向数组 a 中添加 10 个元素
for (int i = 0; i < 10 ; i++)
    a.push_back(i)
//还可以手动调整数组 a 的大小
a.resize(100);
a[90] = 100;
//还可以直接删除数组 a 中所有的元素,此时 a 的长度变为 0
a.clear();
//重新调整 a 的大小为 20,并存储 20 个 -1 元素。
a.resize(20, -1)

六大组件:

        STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator) 。又细分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。

  2. 算法:各种常用的算法,如sort、find、copy、for_each等

  3. 迭代器:扮演了容器与算法之间的胶合剂,STL的算法通过迭代器访问容器

  4. 仿函数:行为类似函数,可作为算法的某种策略。

  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。

        适配器类容器是一种特殊的容器,它可以将一个容器的接口转换成另一个容器的接口,从而使得原本无法兼容的容器能够协同工作。适配器类容器和其他容器的区别在于,它并不是一个完整的容器,而是一个对其他容器进行封装和转换的类。适配器类容器通常包括栈(stack)、队列(queue)和优先队列(priority_queue)等。它们可以使用不同的底层容器来实现,例如vector、deque和list等。适配器类容器的主要作用是提供一种方便的方式来使用不同的容器,同时也可以提高代码的可重用性和可维护性。

  1. 空间配置器:负责空间的配置与管理。

C++刷题--STL 学习笔记(持续更新整合)_第3张图片

【C++ 学习 ⑧】- STL 简介_c++ stl头文件-CSDN博客

十三个头文件:

STL标准库详解_晚安说了吗的博客-CSDN博客

C++ 标准中,STL被组织为 13 个头文件:


算法部分头文件:

        是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。
    

        体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
    

        中则定义了一些模板类,用以声明函数对象。

容器部分头文件:

,,,,,

  • 向量(vector):连续存储的元素
  • 列表(list):由节点组成的双向链表,每个结点包含着一个元素
  • 双队列(deque):连续存储的指向不同元素的指针所组成的数组
  • 集合(set):由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序
  • 多重集合(multiset) :允许存在两个次序相等的元素的集合
  • 栈(stack) :后进先出的值的排列
  • 队列(queue):先进先出的执的排列
  • 优先队列(priority_queue) :元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列
  • 映射(map): 由{键,值}对组成的集合,以某种作用于键对上的谓词排列
  • 多重映射(multimap) :允许键对有相等的次序的映射
  • 无序集合(unordered_set): 不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符
  • 无序多重集合(unordered_multiset):不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符
  • 无序映射(unordered_map):不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符  
  • 无序多重映射(unordered_multimap) :不是使用比较运算符来组织元素的,而是通过一个哈希函数和键类型的==运算符
迭代器头文件:

:是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,

:中提供了迭代器使用的许多方法,而对于的描述则十分的困难,它以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生的临时对象提供机制。

:中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。

STL使用时语法查询网站

C++ STL标准库基础

STL--迭代器

迭代器简介

  • 提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
  • 每个容器都有自己专属的迭代器,如容器vector的迭代器类型: vector::iterator 
  •  迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针,可以像指针一样解引用

迭代器种类(随机/双向/前向)

种类 功能 支持运算
输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写,支持++
前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双向迭代器 读写操作,并能向前和向后操作 读写,支持++、--,
随机访问迭代器 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 读写,支持++、--、[n]、-n、<、<=、>、>=

C++刷题--STL 学习笔记(持续更新整合)_第4张图片

迭代器定义

迭代器定义可以使用auto,省去复杂的迭代器类型书写。

遍历向量
for (vectortest::iterator it = m.begin(); it != m.end(); it++) {
        cout << *it << endl;
}
for(auto it=q.begin();it!=q.end();it++)//这个就是迭代器
{
    cout<<*it<<" ";
}
//注意end()指向最后一个元素的下一个元素

c++ auto基本用法_c++ auto 赋初值_lwgkzl的博客-CSDN博客

迭代器初体验

STL所有容器都有成员函数 .begin()、.end()

    //v.begin()返回迭代器,这个迭代器指向容器中第一个数据
    //v.end()返回迭代器,这个迭代器指向容器元素的最后一个元素的下一个位置【不能解引用】

#include 
#include 

void MyPrint(int val)
{
	cout << val << endl;
}

void test01() {

	//创建vector容器对象,并且通过模板参数指定容器中存放的数据的类型
	vector v;
	//向容器中放数据
	v.push_back(10);
	v.push_back(20);
	v.push_back(30);
	v.push_back(40);

	//每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
	//v.begin()返回迭代器,这个迭代器指向容器中第一个数据
	//v.end()返回迭代器,这个迭代器指向容器元素的最后一个元素的下一个位置
	//vector::iterator 拿到vector这种容器的迭代器类型

	vector::iterator pBegin = v.begin();
	vector::iterator pEnd = v.end();

	//第一种遍历方式:
	while (pBegin != pEnd) {
		cout << *pBegin << endl;
		pBegin++;
	}

	
	//第二种遍历方式:
	for (vector::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << endl;
	}
	cout << endl;

	//第三种遍历方式:
	//使用STL提供标准遍历算法  头文件 algorithm
	for_each(v.begin(), v.end(), MyPrint);
}

int main() {

	test01();

	system("pause");

	return 0;
}

STL--容器

个人从容器特性与使用近似度将容器分为如下类别:

C++刷题--STL 学习笔记(持续更新整合)_第5张图片

容器定义: 基本上就是 STL容器名 <类型> 名字,例如queue q。

容器的种类:

  1、顺序容器:是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。

        顺序容器包括:vector(向量)、list(列表)、deque(队列)。
 

  2、关联容器:关联式容器是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。元素是有序的集合,默认在插入的时候按升序排列。

        关联容器包括:map(字典/映射)、set(集合)、multimap(多重映射)、multiset(多重集合)。

  3、容器适配器:本质上,适配器是使一种不同的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作“它保存一个容器,这个容器再保存所有元素”。

        STL 中包含三种适配器:栈stack 、队列queue 和优先级队列priority_queue 。

        从c的角度,就像骑习惯了自行车的人突然开起了摩托车!!!思考问题维度顺畅的在功能逻辑实现层面流动,而不用时不时陷入每一个具体实现细节。

顺序式容器:

STL--string/字符串

string的用法和下面的正宗STL容器还是有点不一样的,感觉兼具库函数和stl的意思。比如erase的使用,可以直接使用int下标

【C++】——string的功能介绍及使用_c语言string-CSDN博客

C++ string类 使用方法(各种常用函数的介绍)_c++ string类的常用方法-CSDN博客

C++ string类的常用方法(总结)_c++ new string-CSDN博客

基本操作 
//字符串定义与初始化实例:
string s1; //创建空字符串,调用无参构造函数
string s2(str); //把c_string转换成了string
string s4(10, 'a');

//string赋值操作:
str.assign(str1, 2, 3);//从第二个下标开始取3个,若str1=“hello”,结果为"llo".
str.assign("hello c++",5); // 把字符串s的前n个字符赋给当前的字符串
str.assign(str5); // 等价于 “=”
str.assign(5, 'x'); *  //用n个字符c赋给当前字符串

//string子串
s.substr(n,m) //从第n下标开始,截m个
s.substr(n)表示从第n个下标开始截取到尾

//字符串插入和删除。可以直接用下标
str.insert(1, "111");在1号下标位置插入一个字符串
str.erase(1, 3);  //删除从1号位置开始3个字符

//字符串查找子串:find找到子字符串后返回查找的第一个字符位置,找不到返回-1
int pos = str1.find("de");
if (pos == -1)
    cout << "未找到" << endl;

//字符串比较
直接上关系符号 == 、> 、 <

//替换子字符串
str.replace(1, 3, "1111");//从下标1开始替换掉3个字符,用“1111”替换

​​​​​​​//字符串数字转换​
s1 = to_string(int); // 将int数值转为字符串
int x=stoi("1234"); //将字符型1234转为int类型   ---string to int
stoll(str);//字符串转长整数

//字符串追加、拼接
str3.append("game abcde", 4);// 从下标4位置开始,截取末尾,拼接到字符串末尾
str3.append(str2);//相当于"+",str2+str3
str3.append(str2, 4, 3); // 从下标4位置开始 ,截取3个字符,拼接到字符串末尾

//通用
push_back()   //只有这个个容器有这几个函数
pop_back()
clear()
begin()
end()
STL--list_deque_vector--共性:

  由于list_deque_vector三者性质相似,容器的构造初始化、与基本成员函数基本一致,这里先列出所有共性,方便记忆,三者个性后面再分别单列。

常用预览:

增删--插入和删除:

push_back()   //只有这个个容器有这几个函数
pop_back()
push_front()
pop_front()

insert()
clear()
erase()

改--赋值/反转:

赋值:

assign()
=
swap()

查--查看:

size()
empty()
resize()

front()
back()
————————————————
原文链接:https://blog.csdn.net/qq_42676511/article/details/126903894

构造函数与对象初始化

list_deque_vector三者性质相似,构造函数相似,因此容器定义/初始化基本一致,可以一块记忆

  • list_deque_vector lst; //list采用采用模板类实现,对象的默认构造形式:

  • list_deque_vector(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。

注意:[beg, end)区间不仅限于对象容器,例如:

unordered_setset1(v.begin(), v.end());
vectorv(set.begin(), set.end());
  • list_deque_vector(n,elem); //构造函数将n个elem拷贝给本身。

  • list_deque_vector(const list &lst); //拷贝构造函数。

下面对象初始化用 vector 举示例,适用于 list_deque_vector

#include 

void printVector(vector& v) {

	for (vector::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	vector v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vector v2(v1.begin(), v1.end());
	printVector(v2);

	vector v3(10, 100);
    vector v3(10); // 定义的时候指定vector的⼤⼩,默认v3⾥⾯元素都是0
    vector v3{1,2,3};//跟数组差不多
	printVector(v3);
	
	vector v4(v3);
	printVector(v4);
}

int main() {

	test01();

	system("pause");

	return 0;
}
赋值(=很好用)和交换

依然用 vector 举示例,适用于 list_deque_vector: 

//3种方式:
1、push_back
2、=
3、v.assign()

//赋值操作
void test01()
{
	vector v1; //无参构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);

	vectorv2;
	v2 = v1;
	printVector(v2);

	vectorv3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	vectorv4;
	v4.assign(10, 100);
	printVector(v4);
}
插入和删除
* push_back(elem);//在容器尾部加入一个元素
* pop_back();//删除容器中最后一个元素(vector不考虑)
* push_front(elem);//在容器开头插入一个元素
* pop_front();//从容器开头移除第一个元素(vector不考虑)
* insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
* insert(const_iterator pos, int count,ele); //迭代器指向位置pos插入count个元素ele
* insert(const_iterator pos,const_iterator beg,const_iterator end);//在迭代器指向位pos位置插入[beg,end)区间的数据,无返回值。
* clear();//移除容器的所有数据
* erase(const_iterator pos); //删除迭代器指向的元素
* erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素

* resize(int num);             //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
  ​					           //如果容器变短,则末尾超出容器长度的元素被删除。
  • 不同容器使用erase时候的传参是不同的,string传下标,vecoor系传迭代器,set/map传元素,关键点在能明确指定删除对象的前提下尽可能方便。

//insert/erase 会扩展/释放容器内存。 

push_back(),emplace_back()都可以在容器最后添加一个元素。

  • emplace_back是C++11引入的函数,它可以直接在容器中构造对象,而不需要先创建对象再添加,添加一个元素性能更好

push_back和emplace_back的区别只在传入的参数是对象的构造参数时,如push_back(10),emplace_back(10)。

两种情况下:

push_back(10)会先调用类的有参构造函数,创建一个临时对象,再在容器的末尾调用类的右值构造函数(因为这个临时对象没有名字,属于右值,所以只能使用右值构造函数),输入临时对象的地址,创建一个新的对象,根据地址值到临时对象所在的内存空间中拷贝数据,最后再调用临时对象的析构函数,析构这个临时对象。

emplace_back(10)少了临时对象这一步,直接在容器的末尾调用类的有参构造函数创建一个新的对象,完成添加操作。
————————————————
版权声明:本文为CSDN博主「Zeehoy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44179561/article/details/124387475

 下面用 dqueue 举示例,适用于 list_deque_vector: 

//两端操作
void test01()
{
	deque d;
	//尾插
	d.push_back(10);
	d.push_back(20);
	//头插
	d.push_front(100);
	d.push_front(200);

	printDeque(d);

	//尾删
	d.pop_back();
	//头删
	d.pop_front();
	printDeque(d);
}

//插入
void test02()
{
	deque d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);

	d.insert(d.begin(), 1000);
	printDeque(d);

	d.insert(d.begin(), 2,10000);
	printDeque(d);

	dequed2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);

	d.insert(d.begin(), d2.begin(), d2.end());
	printDeque(d);

}

//删除
void test03()
{
	deque d;
	d.push_back(10);
	d.push_back(20);
	d.push_front(100);
	d.push_front(200);
	printDeque(d);

	d.erase(d.begin());
	printDeque(d);

	d.erase(d.begin(), d.end());
	d.clear();
	printDeque(d);
}
查看大小
* empty();                            //判断容器是否为空

* size();                            //返回容器中实际元素个数
* capacity();                        //容器的容量(能容纳的元素个数) 

注意:没有length,这个是string的成员函数,以后统一用 .size()

依然用 vector 举示例,适用于 list_deque_vector: 

void test01()
{
	vector v1;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	printVector(v1);
	if (v1.empty())
	{
		cout << "v1为空" << endl;
	}
	else
	{
		cout << "v1不为空" << endl;
		cout << "v1的容量 = " << v1.capacity() << endl;
		cout << "v1的大小 = " << v1.size() << endl;
	}

	//resize 重新指定大小 ,若指定的更大,默认用0填充新位置,可以利用重载版本替换默认填充
	v1.resize(15,10);
	printVector(v1);

	//resize 重新指定大小 ,若指定的更小,超出部分元素被删除
	v1.resize(5);
	printVector(v1);
}
元素存取
* at(int idx);      //返回索引idx所指的数据(list不考虑,因为不能随机访问)
* operator[];       //返回索引idx所指的数据(list不考虑,因为不能随机访问)
* front();         //返回容器中第一个数据元素
* back();          //返回容器中最后一个数据元素

依然用 vector 举示例,适用于 list_deque_vector: 

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

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

	cout << "v1的第一个元素为: " << v1.front() << endl;
	cout << "v1的最后一个元素为: " << v1.back() << endl;
}
STL--vector/向量

        vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。

vector 常见使用实例-一维存放内置/自定义数据类型

1 vector存放内置数据类型:

#include 
#include 
​
void MyPrint(int val)
{
    cout << val << endl;
}
​
void test01() {
​
    //创建vector容器对象,并且通过模板参数指定容器中存放的数据的类型
    vector v;
    //向容器中放数据
    v.push_back(10);
    v.push_back(20);
    v.push_back(30);
    v.push_back(40);
​
    //每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
    //v.begin()返回迭代器,这个迭代器指向容器中第一个数据
    //v.end()返回迭代器,这个迭代器指向容器元素的最后一个元素的下一个位置
    //vector::iterator 拿到vector这种容器的迭代器类型
​
    vector::iterator pBegin = v.begin();
    vector::iterator pEnd = v.end();
​
    //第一种遍历方式:
    while (pBegin != pEnd) {
        cout << *pBegin << endl;
        pBegin++;
    }
​
    
    //第二种遍历方式:
    for (vector::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }
    cout << endl;
​
    //第三种遍历方式:
    //使用STL提供标准遍历算法  头文件 algorithm
    for_each(v.begin(), v.end(), MyPrint);
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

2 Vector存放自定义数据类型:

#include 
#include 
​
//自定义数据类型
class Person {
public:
    Person(string name, int age) {
        mName = name;
        mAge = age;
    }
public:
    string mName;
    int mAge;
};
//存放对象
void test01() {
​
    vector v;
​
    //创建数据
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
    Person p5("eee", 50);
​
    v.push_back(p1);
    v.push_back(p2);
    v.push_back(p3);
    v.push_back(p4);
    v.push_back(p5);
​
    for (vector::iterator it = v.begin(); it != v.end(); it++) {
        cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;
​
    }
}
​
​
//放对象指针
void test02() {
​
    vector v;
​
    //创建数据
    Person p1("aaa", 10);
    Person p2("bbb", 20);
    Person p3("ccc", 30);
    Person p4("ddd", 40);
    Person p5("eee", 50);
​
    v.push_back(&p1);
    v.push_back(&p2);
    v.push_back(&p3);
    v.push_back(&p4);
    v.push_back(&p5);
​
    for (vector::iterator it = v.begin(); it != v.end(); it++) {
        Person * p = (*it);
        cout << "Name:" << p->mName << " Age:" << (*it)->mAge << endl;
    }
}
​
​
int main() {
​
    test01();
    
    test02();
​
    system("pause");
​
    return 0;
}
vector 常见使用实例-二维向量

定义:

vector< vector >  v;//创建一个空二维数组

vector > ans(m)//这个应该只建立了个指针数组,里面的一维数组内存还没有分配
vector > ans(m,vector(n,1))
vector > ans(m,vector(n))

Vector容器嵌套容器(二维向量):

#include 
​
//容器嵌套容器
void test01() {
​
    vector< vector >  v;//创建一个空二维数组
​
    vector v1;
    vector v2;
    vector v3;
    vector 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);
    }
​
    //将容器元素插入到vector v中
    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);
    v.push_back(v4);
​
​
    for (vector>::iterator it = v.begin(); it != v.end(); it++) {
​
        for (vector::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
            cout << *vit << " ";
        }
        cout << endl;
    }
​
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}
//相当于二维m*n的数组,不过数组的memset好像只能初始化为0
vector > ans(m)
vector > ans(m,vector(n,1))
//行数
int m = ans.size();
//列数
int n = ans[0].size();
for(int i=0;i

vector的swap(v)

//考虑vector的swap的实现是不是 a=b b=c a=c; ??

 // 将vec与本身vector进行元素互换

#include 

void printVector(vector& v) {

	for (vector::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

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

	vectorv2;
	for (int i = 10; i > 0; i--)
	{
		v2.push_back(i);
	}
	printVector(v2);

	//互换容器
	cout << "互换后" << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);
}

void test02()
{
	vector v;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);
	}

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

	v.resize(3);

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

	//收缩内存
	vector(v).swap(v); //匿名对象

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

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

swap可以使两个容器互换,可以达到实用的收缩内存效果。

vector的reserve(len):

reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

总结:如果数据量较大,可以一开始利用reserve预留空间, 减少vector在动态扩展容量时的扩展次数

STL--list/链表 
数据结构 和 实现底层:

        双向链表,每个结点由data、prev指针和next指针构成,头节点的prev指针指向null,尾节点的next指针指向null。
        通过push_back()和pop_back()从尾部添加和删除元素,通过push_front()和pop_front()从头部添加和删除元素。通过insert()从给定位置之前插入元素。
        begin()返回指向链表头的迭代器,end()返回指向链表尾的迭代器。
        不支持随机访问,只能迭代器++或–。

list的reverse()、sort()

//这是list中自带的reverse和sort,不是算法中的reverse和sort。算法中的只支持可随机访问的容器。

void printList(const list& L) {

	for (list::const_iterator it = L.begin(); it != L.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

bool myCompare(int val1 , int val2)
{
	return val1 > val2;
}

//反转和排序
void test01()
{
	list L;
	L.push_back(90);
	L.push_back(30);
	L.push_back(20);
	L.push_back(70);
	printList(L);

	//反转容器的元素
	L.reverse();
	printList(L);

	//排序
	L.sort(); //默认的排序规则 从小到大
	printList(L);

	L.sort(myCompare); //指定规则,从大到小
	printList(L);
}

int main() {

	test01();

	system("pause");

	return 0;
}

经典排序案例: 

#include 
#include 
class Person {
public:
	Person(string name, int age , int height) {
		m_Name = name;
		m_Age = age;
		m_Height = height;
	}

public:
	string m_Name;  //姓名
	int m_Age;      //年龄
	int m_Height;   //身高
};


bool ComparePerson(Person& p1, Person& p2) {

	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height  > p2.m_Height;
	}
	else
	{
		return  p1.m_Age < p2.m_Age;
	}

}

void test01() {

	list L;

	Person p1("刘备", 35 , 175);
	Person p2("曹操", 45 , 180);
	Person p3("孙权", 40 , 170);
	Person p4("赵云", 25 , 190);
	Person p5("张飞", 35 , 160);
	Person p6("关羽", 35 , 200);

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

	for (list::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}

	cout << "---------------------------------" << endl;
	L.sort(ComparePerson); //排序

	for (list::iterator it = L.begin(); it != L.end(); it++) {
		cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age 
              << " 身高: " << it->m_Height << endl;
	}
}

int main() {

	test01();

	system("pause");

	return 0;
}
STL--deque
栈和队列详解(知识点+STL实现+相关LeetCode题目)_leetcode使用stl-CSDN博客

适配器容器:

【C++】stack和queue的经典题目&模拟实现@STL_c++ stl queue 题-CSDN博客

        适配器类容器和其他容器的区别在于,它并不是一个完整的容器,而是一个对其他容器进行封装和转换的类。适配器类容器通常包括栈(stack)、队列(queue)和优先队列(priority_queue)等。它们可以使用不同的底层容器来实现,例如vector、deque和list等。

        几个适配器容器都没有迭代器。迭代器最大的作用是能够依序寻访某个容器所含的各个元素,而栈和队列根据数据结构,只能访问其 “端元素”,因此有迭代器没意义。

STL--stack/

        栈和队列都不支持迭代器,各自通过top、front/back 访问端元素。

构造函数:

  • stack stk; //stack采用模板类实现, stack对象的默认构造形式

  • stack(const stack &stk); //拷贝构造函数

赋值操作:

  • stack& operator (const stack &stk); //重载等号操作符

数据存取:

  • push(elem); //向栈顶添加元素

  • pop(); //从栈顶移除第一个元素,不返回移除函数的值

  • top(); //返回栈顶元素

大小操作:

  • empty(); //判断堆栈是否为空

  • size(); //返回栈的大小

#include 
#include 
using namespace std;
int main() {
    stack s; // 定义⼀个空栈s
    for (int i = 0; i < 6; i++) {
        s.push(i); // 将元素i压⼊栈s中
    }
    cout << s.top() << endl; // 访问s的栈顶元素
    cout << s.size() << endl; // 输出s的元素个数
    s.pop(); // 移除栈顶元素
    return 0;
}

STL--queue/队列

构造函数:

  • queue que; //queue采用模板类实现,queue对象的默认构造形式

  • queue(const queue &que); //拷贝构造函数

赋值操作:

  •  //重载等号操作符

数据存取:

  • push(elem); //往队尾添加元素

  • pop(); //从队头移除第一个元素

  • back(); //返回最后一个元素

  • front(); //返回第一个元素

大小操作:

  • empty(); //判断堆栈是否为空

  • size(); //返回栈的大小

STL--priority_queue/优先队列

        优先队列不是按照入队顺序排序,而是按照指定顺序,优先级最高的元素最先出队。且只能访问队列首部的元素,优先队列往往用 堆 来实现。

priority_queue q;

//基本操作成员函数类似栈
q.empty()          如果队列为空,则返回true,否则返回false 
q.size()           返回队列中元素的个数
q.pop()            删除队首元素,但不返回其值

q.top()            返回具有最高优先级的元素值,但不删除该元素

q.push()       在基于优先级的适当位置插入新元素

STL源码剖析-priority_queue_priority_queue 有迭代器吗-CSDN博客

基本使用:
priority_queue
Type:数据类型
Container:容器
Functional:比较方式
  • 使用基本数据类型时,只需要传入数据类型,默认是升序,当需要用自定义的数据类型和排序规则时才需要传入三个参数。
  • Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list,STL默认用的是vector。
  • 默认是使用大顶堆的,即队首总是最大的元素(从队尾到队首是升序)

内置数据类型:

priority_queue a; 
等同于:
priority_queue,less> a;
//priority_queue,greater> q;

priority_queue q;//储存int型数据 
priority_queue q;//储存double型数据 
priority_queue q;//储存string型数据

priority_queue,greater> q;//储存int型数据
priority_queue,greater> q;//储存double型数据
priority_queue,greater> q;//储存string型数据
//https://blog.csdn.net/weixin_52115456/article/details/127606811?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170356379216800197096994%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=170356379216800197096994&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-127606811-null-null.142^v96^pc_search_result_base7&utm_term=c%2B%2B%20priority_queue%E9%A2%98&spm=1018.2226.3001.4187

结构体实例:

struct cmp{
    bool operator() (fruit f1, fruit f2){
        return f1.price > f2.price;
    }
};

struct fruit{
    string name;
    int price;
}; 
 
此时定义优先队列:
priority_queue, cmp>obj;

//代码来源:https://blog.csdn.net/weixin_53432918/article/details/134191839?ops_request_misc=&request_id=&biz_id=102&utm_term=leetcode%20priority_queue%E9%A2%98&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-5-134191839.142^v96^pc_search_result_base7&spm=1018.2226.3001.4187
leetcode题目

347. 前 K 个高频元素

LCR 078. 合并 K 个升序链表

871. 最低加油次数

关联式容器:

STL--set/集合

        set是集合,⼀个set⾥⾯的各元素是各不相同的,⽽且默认情况下set会按照元素进⾏从⼩到⼤排序。

        它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。

构造和赋值:

    set s1;
    sets2(s1);

    //赋值
    sets3;
    s3 = s2;

大小和交换:

  • size(); //返回容器中元素的数目

  • empty(); //判断容器是否为空

  • swap(st); //交换两个集合容器

插入和删除:

  • insert(elem); //在容器中插入元素。返回值是pair::iterator,bool>ret=s.insert(10);

  • clear(); //清除所有元素

  • erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。

  • erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

  • erase(elem); //删除容器中值为elem的元素。

查找和统计元素:
  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();

  • count(key); //统计key的元素个数

set的⽤法示例:

#include 
#include 
using namespace std;
int main() {
    set s;//不要像vector一样指定集合的大小,没有大小!!
    s.insert(2);//向集合添加元素
    s.insert(3);//向集合添加元素
    cout << *(s.begin()) << endl; //输出第一个元素
    for (int i = 0; i < 10; i++) {//插入0 - 9
        s.insert(i);
    }
    for (set::iterator it = s.begin(); it != s.end(); it++) {
        cout << *it << " ";//集合的遍历,it是一个迭代的指针
    }
    cout << endl << (s.find(2) != s.end()) << endl;//查找,元素
    s.erase(3);//删除元素
    cout << (s.find(3) != s.end()) << endl;
    return 0;
}
set的排序 

示例一 set存放内置数据类型

#include 
​
class MyCompare 
{
public:
    bool operator()(int v1, int v2) {
        return v1 > v2;
    }
};
void test01() 
{    
    set s1;
    s1.insert(10);
    s1.insert(40);
    s1.insert(20);
    s1.insert(30);
    s1.insert(50);
​
    //默认从小到大
    for (set::iterator it = s1.begin(); it != s1.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
​
    //指定排序规则
    set s2;
    s2.insert(10);
    s2.insert(40);
    s2.insert(20);
    s2.insert(30);
    s2.insert(50);
​
    for (set::iterator it = s2.begin(); it != s2.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}
​
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

总结:利用仿函数可以指定set容器的排序规则

示例二 set存放自定义数据类型

#include 
#include 
​
class Person
{
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
​
    string m_Name;
    int m_Age;
​
};
class comparePerson
{
public:
    bool operator()(const Person& p1, const Person &p2)
    {
        //按照年龄进行排序  降序
        return p1.m_Age > p2.m_Age;
    }
};
​
void test01()
{
    set s;
​
    Person p1("刘备", 23);
    Person p2("关羽", 27);
    Person p3("张飞", 25);
    Person p4("赵云", 21);
​
    s.insert(p1);
    s.insert(p2);
    s.insert(p3);
    s.insert(p4);
​
    for (set::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
    }
}
int main() {
​
    test01();
​
    system("pause");
​
    return 0;
}

总结:对于自定义数据类型,set必须指定排序规则才可以插入数据

multiset

允许容器中有重复的元素

  • set不可以插入重复数据,而multiset可以

  • set插入数据的同时会返回插入结果,表示插入是否成功

  • multiset不会检测数据,因此可以插入重复数据

undered_set:

就是不排序的set

         unordered_set 容器具有以下几个特性:

  1. 不再以键值对的形式存储数据,而是直接存储数据的值;
  2. 容器内部存储的各个元素的值都互不相等,且不能被修改。
  3. 不会对内部存储的数据进行排序(这和该容器底层采用哈希表结构存储数据有关,可阅读《C++ STL无序容器底层实现原理》一文做详细了解);

      unordered_map内部实现了一个哈希表,也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用。因此,其元素的排列顺序是无序的。
        unordered_set底层也是哈希表,只是存储的是value,而不是

STL--map/字典
pair对组创建(前置知识点)

功能描述:

  • pair是将2个数据组合成一组数据,如stl中的map就是将key和value放在一起来保存,或者当一个函数需要返回2个数据的时候,也可以选择pair。

  • pair的实现是一个结构体,主要的两个成员变量是first second 。因为是使用struct不是class,所以可以直接使用pair的成员变量。

头文件:

C++ pair的基本用法总结(整理)-CSDN博客

两种创建方式:

  • pair p ( value1, value2 );

  • pair p = make_pair( value1, value2 );

示例:

pair p(string("Tom"), 20);​

pair p2 = make_pair("Jerry", 10);

两种方式都可以创建对组,记住一种即可

*map

概念:

  • map中所有元素都是pair

  • pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)。key 和 value可以是任意你需要的类型。

  • 所有元素都会根据元素的键值自动排序

本质:

  • map/multimap属于关联式容器,底层结构是用二叉树实现。增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。

优点:

  • 可以根据key值快速找到value值,查找的复杂度基本是Log(N)。

 map的一套成员函数和set基本一致:

构造和赋值:

//默认构造
mapm; 
m.insert(pair(1, 10));
m.insert(pair(2, 20));

//拷贝构造
mapm2(m); 

//赋值
mapm3;
m3 = m2; 

大小和交换:

  • size(); //返回容器中元素的数目

  • empty(); //判断容器是否为空

  • swap(st); //交换两个集合容器

插入和删除:

  • insert(elem); //在容器中插入元素。

  • clear(); //清除所有元素

  • erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。

  • erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。

  • erase(elem); //删除容器中值为elem的元素。

void test01()
{
	//插入
	map m;
	//第一种插入方式
	m.insert(pair(1, 10));
	//第二种插入方式
	m.insert(make_pair(2, 20));
	//第三种插入方式
	m[4] = 40; 
	printMap(m);

	//删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);//根据键值删除 pair
	printMap(m);

	//清空
	m.erase(m.begin(),m.end());
	m.clear();
	printMap(m);
}
查找和统计元素:
  • find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();

  • count(key); //统计key的元素个数(显然,对于map,结果为0或者1)

map的排序:
  • map容器默认排序规则为 按照key值进行 从小到大排序(不能按照value排序),利用仿函数,可以改变排序规则
  • 对于自定义数据类型,map必须要指定排序规则

 map排序示例:

#include 

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

void test01() 
{
	//默认从小到大排序
	//利用仿函数实现从大到小排序
	map m;

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

	for (map::iterator it = m.begin(); it != m.end(); it++) {
		cout << "key:" << it->first << " value:" << it->second << endl;
	}
}
int main() {

	test01();

	system("pause");

	return 0;
}

map基本操作示例 1:

#include
#include
using namespace std;
mapmp;//定义map容器 
int main()
{
	//数组方式插入 
	mp[1]='a';
	mp[1]='b';//key不允许重复,再次插入相当于修改value的值 
	mp[2]='a'; 
	mp[3]='b';
	mp[4]='c';
	cout<<"根据key值输出对应的value值"<::value_type(5,'d')); 
	
	//输出容器大小 
	int s=mp.size();
	cout<::iterator it=mp.begin();
	while(it!=mp.end())
	{
		cout<<"key:"<first<<" "; 
		cout<<"value:"<second<first<second<

map基本操作示例 2:

#include 
#include 
using namespace std;
int main()
{
	map  m;//创建名为m的键值对 
	m["hello"]=2;
	m["world"]=3;
	m["apple"]=4;
	cout<first<<" "<second<,map也用p-> 
	//p->first就是键,p->second就是值 
	cout<<"m的长度为 "<

运行结果:  按key从小到大帮你自动排好,appleC++刷题--STL 学习笔记(持续更新整合)_第6张图片

multimap

        multimap允许容器中有重复key值元素

unodered_map:

就是不排序的map

#include
特点:
key的值不会重复,key的值不会排序,但是会去重。
优点:底层结构是哈希表,查找0(1),效率高。
缺点:哈希表的建立耗费时间。
应用场景:对于频繁的查找,用unordered_map更高效。
底层是一个哈希表值。
unordered_map记录元素的hash值,根据hash值判断元素是否相同。

STL容器横向对比:

待看: 

STL基础4:STL7个常用容器的比较_stl 容器对比-CSDN博客

STL各容器对比_stl容器对比-CSDN博客

 【C++】STL相关容器的对比_vectordequelistsetmultisetmapmultimap-CSDN博客

STL--算法

结合容器的迭代器,有很多算法可以使用,下面介绍常用的:

遍历算法:

for_each在实际开发中是最常用遍历算法,常用容器都可用其遍历,需要熟练掌握。

for_each()
for_each(iterator beg, iterator end, _func);
// 遍历算法 遍历容器元素

示例:

//普通函数
//void print01(int& val) //也可以改值
void print01(int val) 
{
	cout << val << " ";
}
//函数对象
class print02 
{
 public:
	void operator()(int val) 
	{
		cout << val << " ";
	}
};

//遍历算法
for_each(v.begin(), v.end(), print01);
cout << endl;

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

既然有for循环,为什么需要 for_each函数? 

【C++】泛型算法之std::for_each_for的头文件-CSDN博客

c++中的 for_each 函数_c++ foreach_%LMX%的博客-CSDN博客

for
for不是算法,这里只是展示一下刷题时常用的简洁方式
//方式1:遍历数组
int nums[] = { 3, 4, 2, 9, 15, 267 };
const int N = 6;

//for (int &var : nums) {	// 这里可以使用引用"&"来修改数组中的值
for (int var : nums) {
	cout << var << endl;
}

//方式2:遍历容器
vector vec(5,1);

//for (int &var : vec) {	// 这里可以使用引用"&"来修改容器中的值
for (int var : vec) {
	cout << var << endl;
}

查找/统计算法:

       查找/统计算法的设计针对于没有成员函数find的序列式容器,关联式容器和string有自己的find()成员函数。

find()
find(iterator beg, iterator end, value);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置

示例:

#include 
#include 
#include 
void test01() {

	vector v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i + 1);
	}
	//查找容器中是否有 5 这个元素
	vector::iterator it = find(v.begin(), v.end(), 5);
	if (it == v.end()) 
	{
		cout << "没有找到!" << endl;
	}
	else 
	{
		cout << "找到:" << *it << endl;
	}
}

class Person {
public:
	Person(string name, int age) 
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	//重载==
	bool operator==(const Person& p) 
	{
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) 
		{
			return true;
		}
		return false;
	}

public:
	string m_Name;
	int m_Age;
};

void test02() {

	vector v;

	//创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);

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

	vector::iterator it = find(v.begin(), v.end(), p2);
	if (it == v.end()) 
	{
		cout << "没有找到!" << endl;
	}
	else 
	{
		cout << "找到姓名:" << it->m_Name << " 年龄: " << it->m_Age << endl;
	}
}

用法总结:

  • 几乎所有的C++容器都可以使用std::find进行查找,但是只有关联容器和string提供了成员函数find。
  • find函数都是查找到对应元素并返回指向该元素的迭代器/元素本身索引值,如果查找不到,除了string之外的容器都是返回s.end(),但是string是返回string::npos.
  • 对于关联容器(如 std::set, std::map 等),find 函数找到元素时返回的是一个迭代器,该迭代器指向找到的元素,而不是索引。关联容器中元素的顺序并不是按照它们在容器中的插入顺序,而是按照元素的值来确定。因此,对于关联容器来说,"索引"的概念并不适用。
  • 对于 std::string,find 函数找到字符或子串时,返回的是该字符或子串在 std::string 中的位置(索引)
  • 对于顺序容器(如 std::vector, std::list 等),它们没有成员函数 find。但你可以使用非成员函数 std::find 来在这些容器中查找元素。std::find 找到元素时返回的是一个迭代器,该迭代器指向找到的元素。

————————————————
原文链接:https://blog.csdn.net/CFY1226/article/details/131901268

 find_if()
find_if(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置

 示例:

#include 
#include 
#include 

//内置数据类型
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};

void test01() {

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

	vector::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end()) {
		cout << "没有找到!" << endl;
	}
	else {
		cout << "找到大于5的数字:" << *it << endl;
	}
}

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

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

};

void test02() {

	vector v;

	//创建数据
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);

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

	vector::iterator 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();

	system("pause");

	return 0;
}
 count()
count(iterator beg, iterator end, value);
// 统计元素出现次数

示例:

#include 
#include 

//内置数据类型
void test01()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count(v.begin(), v.end(), 4);

	cout << "4的个数为: " << num << endl;
}

//自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	bool operator==(const Person & p)
	{
		if (this->m_Age == p.m_Age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	string m_Name;
	int m_Age;
};

void test02()
{
	vector v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
    
    Person p("诸葛亮",35);

	int num = count(v.begin(), v.end(), p);
	cout << "num = " << num << endl;
}
int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}
count_if()
count_if(iterator beg, iterator end, _Pred);
// 按条件统计元素出现次数

示例:

#include 
#include 

class Greater4
{
public:
	bool operator()(int val)
	{
		return val >= 4;
	}
};

//内置数据类型
void test01()
{
	vector v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(5);
	v.push_back(3);
	v.push_back(4);
	v.push_back(4);

	int num = count_if(v.begin(), v.end(), Greater4());

	cout << "大于4的个数为: " << num << endl;
}

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

	string m_Name;
	int m_Age;
};

class AgeLess35
{
public:
	bool operator()(const Person &p)
	{
		return p.m_Age < 35;
	}
};
void test02()
{
	vector v;

	Person p1("刘备", 35);
	Person p2("关羽", 35);
	Person p3("张飞", 35);
	Person p4("赵云", 30);
	Person p5("曹操", 25);

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

	int num = count_if(v.begin(), v.end(), AgeLess35());
	cout << "小于35岁的个数:" << num << endl;
}


int main() {

	//test01();

	test02();

	system("pause");

	return 0;
}
binary_search

binary_search算法可以在有序容器中查找某个元素。

vector v = {1, 2, 3};
bool found = binary_search(v.begin(), v.end(), 2); // found: true

排序算法:

 什么容器/场景需要用到STL的std::sort()、std::reverse()?

  • STL的所有关联型容器都有自动排序的功能(底层结构采取红黑树),所以不需要用到这个sort算法。
  • 至于序列式容器中的stack、queue和priority-queue都有特定的出入口,不允许用户对元素进行排序。
  • 排序算法的设计针对于适用于支持随机访问的容器:string,vector,deque;list的迭代器属于双向迭代,需要自己开发sort成员函数。

 ———————————————— 

原文链接:https://blog.csdn.net/Student_xiao_ming/article/details/88313013

 sort()
sort(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置

 示例:

#include 
#include 

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

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

	//sort默认从小到大排序
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;

	//从大到小排序
	sort(v.begin(), v.end(), greater());
	for_each(v.begin(), v.end(), myPrint);
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}
 reverse()
reverse(iterator beg, iterator end);
// 反转指定范围的元素

 示例:

#include 
#include 

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

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

	cout << "反转前: " << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	cout << "反转后: " << endl;

	reverse(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

常用拷贝和替换算法:

常见容器都可以用,不过string有自己的copy、replace成员函数

copy

 容器内指定范围的元素拷贝到另一容器中

vector v1 = {1, 2, 3};
vector v2(3);
copy(v1.begin(), v1.end(), v2.begin()); // v2: {1, 2, 3}
replace

将容器内指定范围的旧元素修改为新元素

replace(iterator beg, iterator end, oldvalue, newvalue);
// 将区间内旧元素 替换成 新元素
replace_if

容器内指定范围满足条件的元素替换为新元素

replace_if(iterator beg, iterator end, _pred, newvalue);

// 按条件替换元素,满足条件的替换成指定元素
// beg 开始迭代器
// end 结束迭代器
// _pred 谓词
// newvalue 替换的新元素

示例: 

#include 
#include 

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

class ReplaceGreater30
{
public:
	bool operator()(int val)
	{
		return val >= 30;
	}

};

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

	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;

	//将容器中大于等于的30 替换成 3000
	cout << "替换后:" << endl;
	replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
	for_each(v.begin(), v.end(), myPrint());
	cout << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}

去重算法:

unique
vector v = {1, 1, 2, 2};
auto it = unique(v.begin(), v.end());
v.erase(it, v.end()); // v: {1, 2}

 该函数的作用是“去除”容器或者数组中相邻元素的重复出现的元素,注意
        (1) 这里的去除并非真正意义的erase,而是将重复的元素放到容器的末尾,返回值是去重之后的尾地址。
        (2) unique针对的是相邻元素,所以对于顺序顺序错乱的数组成员,或者容器成员,需要先进行排序,可以调用std::sort()函数。

数学算数算法:

使用时包含的头文件为 #include

求和 accumulate()
accumulate(iterator beg, iterator end, value);
//计算容器元素累计总和
填充 fill()
fill(iterator beg, iterator end, value);
// 向容器中填充元素

集合(求 交、并、差)算法

注意:不是只有set才能用

三个函数 共性关键点强调:

注意:   1、两个容器必须是有序序列
           2、三个函数的返回值是有效输出范围的结束符迭代器,常需要再调整新容器尺寸:stl.resize(it - res.begin())
,否则可能出现多余的填充值0;

// beg1 容器1开始迭代器
// end1 容器1结束迭代器
// beg2 容器2开始迭代器
// end2 容器2结束迭代器


// dest 目标容器开始迭代器

注:第五的参数为 iterator dest,这要求容器 dest 必须先被分配好了足够的空间。

set_intersection // 求两个容器的交集

set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

//目标容器开辟空间需要从两个容器中取小值

 示例:

// set_intersection example
#include      // std::cout
#include     // std::set_intersection, std::sort
#include        // std::vector

int main () {
  int first[] = {5,10,15,20,25};
  int second[] = {50,40,30,20,10};
  std::vector v(10);                      // 0  0  0  0  0  0  0  0  0  0
  std::vector::iterator it;

  std::sort (first,first+5);     //  5 10 15 20 25
  std::sort (second,second+5);   // 10 20 30 40 50

  it=std::set_intersection (first, first+5, second, second+5, v.begin());
                                               // 10 20 0  0  0  0  0  0  0  0
  v.resize(it-v.begin());                      // 10 20

  std::cout << "The intersection has " << (v.size()) << " elements:\n";
  for (it=v.begin(); it!=v.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}
set_union // 求两个容器的并集

set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

//目标容器开辟空间需要两个容器相加

示例:

// set_union example
#include      // std::cout
#include     // std::set_union, std::sort
#include        // std::vector

int main () {
  int first[] = {5,10,15,20,25};
  int second[] = {50,40,30,20,10};
  std::vector v(10);                      // 0  0  0  0  0  0  0  0  0  0
  std::vector::iterator it;

  std::sort (first,first+5);     //  5 10 15 20 25
  std::sort (second,second+5);   // 10 20 30 40 50

  it=std::set_union (first, first+5, second, second+5, v.begin());
                                               // 5 10 15 20 25 30 40 50  0  0
  v.resize(it-v.begin());                      // 5 10 15 20 25 30 40 50
  std::cout << "The union has " << (v.size()) << " elements:\n";
  for (it=v.begin(); it!=v.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';
  return 0;
}
set_difference // 求两个容器的差集 

set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);

//目标容器开辟空间需要从两个容器取较大值

举例:

  • v1容器: 0 1 2 3 4 5 6 7 8 9            v2容器:5 6 7 8 9 10 11 12 13 14

则:

  • v1和v2的差集: 0 1 2 3 4
  • v2和v1的差集: 10 11 12 13 14

        值得注意一句话: 三个函数的返回值既是交集中是 指向有效输出范围结尾的迭代器,如果不resize新容器,则容器的末尾很可能包含0。

// set_difference example
#include      // std::cout
#include     // std::set_difference, std::sort
#include        // std::vector

int main () {
  int first[] = {5,10,15,20,25};
  int second[] = {50,40,30,20,10};
  std::vector v(10);                      // 0  0  0  0  0  0  0  0  0  0
  std::vector::iterator it;

  std::sort (first,first+5);     //  5 10 15 20 25
  std::sort (second,second+5);   // 10 20 30 40 50

  it=std::set_difference (first, first+5, second, second+5, v.begin());
                                               //  5 15 25  0  0  0  0  0  0  0
  v.resize(it-v.begin());                      //  5 15 25

  std::cout << "The difference has " << (v.size()) << " elements:\n";
  for (it=v.begin(); it!=v.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

其他

cctype头文件的一些好用小函数

        头文件#include ,万能头文件#include 依旧支持cctype包括的这些函数

isalpha()判断此字符是否为字母
islower()判断此字符是否为小写字母
isupper()判断此字符是否为大写字母
isalnum()判断此字符是否为字母或数字
isspace()判断此字符是否为空格或转换符
如果是则输出1,不是就输出0
tolower()将此字符转为小写字母
toupper()将此字符转为大写字母
#include 
//#include 
#include 
using namespace std;
int main()
{
    char c='A';
    cout<<"isalpha:"<

运行结果: 

C++刷题--STL 学习笔记(持续更新整合)_第7张图片

c++刷题实践总结

1 排序

C++刷题实践--排序总结-CSDN博客

2 c++STL使用时的迭代器失效问题

参考链接

核心参考:

再也不用c刷题了!!——c++刷题必备_刷题用c++_ZZULI_星.夜的博客-CSDN博客

超详细C转C++简单教程(算法竞赛所需)_青霞先生的博客-CSDN博客

C++ STL算法库中20种常用的算法,刷题必会_yingjiejk的博客-CSDN博客

待后续参考:

(9条消息) C++中STL用法超详细总结_一只大笨猫的博客-CSDN博客_c++ stl

(9条消息) C++ STL常用标准库容器入门(vector,map,set,string,list...)_yuleidnf的博客-CSDN博客

C++_STL—算法Algorithm篇_老陈聊架构的博客-CSDN博客

C/C++常用函数合集_c++函数_li_il的博客-CSDN博客

你可能感兴趣的:(c语言,c++,开发语言)