C++ STL常用容器:string、vector、deque、stack、queue、list、set/multiset、map/multimap

文章目录


0 STL主要容器的特点

容器名称底层数据结构特点迭代器类型容器类型
string字符串(动态)字符数组支持随机访问
动态扩展容量
随机访问迭代器序列式容器
vector单端数组/
动态数组
(动态)数组支持随机访问
动态扩展容量
随机访问迭代器序列式容器
deque双端数组/
双端队列
中央控制器
多个数据缓冲区
支持随机访问
首尾快速插入和删除元素
随机访问迭代器序列式容器
list链表双向循环链表快速插入和删除元素双向迭代器序列式容器
stacklist或deque(封闭一端)元素先进后出
仅栈顶元素可被外部访问
不支持迭代器序列式容器
queue队列list或deque元素先进先出
仅队首和队尾元素可被外部访问
不支持迭代器序列式容器
priority_queue优先队列vector(以堆的处理规则管理)
本质为大根堆小根堆
堆顶元素/队首元素的优先级最大不支持迭代器序列式容器
set /
multiset
集合红黑树元素有序、不可重复 /
元素有序、可重复
双向迭代器关联式容器
map /
multimap
映射表红黑树键值对形式,元素有序、不可重复 /
键值对形式,元素有序、可重复
双向迭代器关联式容器
unordered_set /
unordered_multiset
无序集合哈希表元素无序、不可重复 /
元素无序、可重复
双向迭代器关联式容器
unordered_map /
unordered_multimap
无序映射表哈希表键值对形式,元素无序、不可重复 /
键值对形式,元素无序、可重复
双向迭代器关联式容器

注1:支持随机访问迭代器的容器,可使用索引/下标跳跃式地随机访问容器中的元素,如索引+n。
注2:string容器的原理类似于vector,内部使用动态数组的方式实现,但仅能存储字符元素
(可理解为:string容器是只能存储char类型字符元素的vector)。
注3:stackqueuepriority_queue本质是适配器,是对容器的再封装(包装)。
优先队列priority_queue的本质为大根堆小根堆,插入或删除元素的时间复杂度为O(log n)。


1 string容器

1.1 string基本概念

string和char *的区别
string:C++风格的字符串;本质是。string类内部封装char*成员属性,是char*型容器,底层维护C语言的字符串。
char *:C风格的字符串;本质是指针

特点
(1)string类的内部封装了许多用于操作字符串成员方法,例如:查找find、拷贝copy、删除delete、替换replace、插入insert等。
(2)string类管理char*的内存分配时,无需担心复制越界、取值越界、内存释放(析构调用)等,由string类的内部负责管理。


1.2 string构造函数

构造函数原型
(1)string();:默认无参构造函数,创建一个空字符串,如string str;
(2)string(const char* s); :有参构造函数,使用C语言字符串s初始化一个C++风格字符串
(3)string(const string& str);:拷贝构造函数,使用已有string对象初始化新的string对象。
(4)string(int n, char c);:有参构造函数,使用n个字符c初始化一个C++风格字符串

注:string的多种构造方式之间无可比性,灵活使用即可。

示例:string的构造函数

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

int main() {
	/* 默认无参构造 */
	string str1;
	cout << "str1 = " << str1 << endl;	//(空字符串)

	/* 有参构造:使用C风格的const char*字符串初始化 */
	const char* cstr = "hello cpp";
	string str2(cstr);
	cout << "str2 = " << str2 << endl;	//hello cpp

	/* 拷贝构造 */
	string str3(str2);
	cout << "str3 = " << str3 << endl;	//hello cpp

	/* 多个字符拼接 */
	string str4(5, 'h');
	cout << "str4 = " << str4 << endl;	//hhhhh

	return 0;
}

1.3 字符串长度【size()、length()、strlen()】

(1)const char*类型的字符串长度
使用函数strlen(const char* s)获取字符串长度不包含'\0'结束符)。
函数原型unsigned int strlen(const char *pStr);

注1:运算符sizeof("xxx")计算的是占用的内存空间大小包含'\0'结束符),而不是字符串长度。
注2:单个英文字符的长度为1;单个中文字符的长度为2

//const char*字符串的长度-英文字符占1个字符长度
cout << strlen("world") << endl;		//字符串长度:5(未计算'\0')
cout << sizeof("world") << endl;		//内存大小:6字节(计算'\0')
									
//const char*字符串的长度-中文字符占2个字符长度
cout << strlen("世界和中国") << endl;	//字符串长度:10(未计算'\0')
cout << sizeof("世界和中国") << endl;	//内存大小:11字节(计算'\0')

(2)string类型的字符串长度
①使用成员函数size()length()获取字符串长度不包含'\0'结束符)。
string类型对象可调用成员函数c_str()转换为const char*类型,再使用strlen(const char* s)获取字符串长度,即strlen(str.c_str())
函数声明const char* string::c_str() const;

//string字符串的长度-英文字符占1个字符长度
string str1 = "hello";
cout << "str1的长度:" << str1.length() << endl;			//5
cout << "str1的长度:" << str1.size() << endl;			//5
cout << "str1的长度:" << strlen(str1.c_str()) << endl;	//5

//string字符串的长度-中文字符占2个字符长度
string str2 = "世界和中国";
cout << "str2的长度:" << str2.length() << endl;			//10
cout << "str2的长度:" << str2.size() << endl;			//10
cout << "str2的长度:" << strlen(str2.c_str()) << endl;	//10

1.4 string赋值操作【operator=、assign()】

作用:通过重载赋值运算符operator=成员函数assign(),对string字符串进行赋值。

注:string的赋值操作,重载赋值运算符operator=的方式更常用

赋值操作的函数原型
(1)string& operator=(const char* s);:使用const char*类型的字符串,对当前字符串赋值。
(2)string& operator=(const string &s); :使用string类型的字符串s,对当前字符串赋值。
(3)string& operator=(char c);:使用单个字符,对当前字符串赋值。


(4)string& assign(const char *s);:使用const char*类型的字符串,对当前字符串赋值。
(5)string& assign(const char *s, int n);:使用const char*类型的字符串s的前n个字符(子串),对当前字符串赋值。
(6)string& assign(const string &s);:使用string类型的字符串,对当前字符串赋值。
(7)string& assign(int n, char c);:使用n个字符c,对当前字符串赋值。

示例:string的赋值操作

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

int main() {
	/* 重载赋值运算符operator=赋值 */
	//(1)string& operator=(const char* s); :使用const char* 类型的字符串,对当前字符串赋值。
	string str1;
	str1 = "hello";
	cout << "str1 = " << str1 << endl;	//hello

	//(2)string& operator=(const string& s); :使用string类型的字符串s,对当前字符串赋值。
	string str2;
	str2 = str1;
	cout << "str2 = " << str2 << endl;	//hello

	//(3)string& operator=(char c); :使用单个字符,对当前字符串赋值。
	string str3;
	str3 = 'a';
	cout << "str3 = " << str3 << endl;	//a

	//(4)string& assign(const char* s); :使用const char* 类型的字符串,对当前字符串赋值。
	string str4;
	str4.assign("world");
	cout << "str4 = " << str4 << endl;	//world

	//(5)string& assign(const char* s, int n); :使用const char* 类型的字符串s的前n个字符(子串),对当前字符串赋值。
	string str5;
	str5.assign("world", 2);
	cout << "str5 = " << str5 << endl;	//wo
	
	//(6)string& assign(const string & s); :使用string类型的字符串,对当前字符串赋值。
	string str6;
	str6.assign(str5);
	cout << "str6 = " << str6 << endl;	//wo
	
	//(7)string& assign(int n, char c); :使用n个字符c,对当前字符串赋值。
	string str7;
	str7.assign(5, 'h');
	cout << "str7 = " << str7 << endl;	//hhhhh

	return 0;
}

1.5 string字符串拼接【operator+=、append()】

作用:通过重载加法赋值运算符operator+=成员函数append(),实现在源字符串的末尾拼接目标字符串(尾部追加)。

字符串拼接操作的函数原型
(1)string& operator+=(const char* str);:将const char*类型的字符串,拼接至原字符串结尾。
(2)string& operator+=(const char c);:将单个字符,拼接至原字符串结尾。
(3)string& operator+=(const string& str);:将string类型的字符串,拼接至原字符串结尾。


(4)string& append(const char *s);:将const char*类型的字符串,拼接至原字符串结尾。
(5)string& append(const char *s, int n);:将const char*类型的字符串s的前n个字符,拼接至原字符串结尾。
(6)string& append(const string &s);:将string类型的字符串,拼接至原字符串结尾。
(7)string& append(const string &s, int pos, int n);:将string类型的字符串s中自索引位置pos起的n个字符(长度为n子串),拼接至原字符串结尾。

注:成员函数append(const string &s, int pos, int n);截取的字符串,包含索引位置pos本身的字符

示例:string的字符串拼接操作

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

/* 字符串拼接 */
int main() {
	//(1)string& operator+=(const char* str); 
	//将const char* 类型的字符串,拼接至原字符串结尾。
	string str1 = "我";
	str1 += "爱学习";
	cout << "str1 = " << str1 << endl;	//我爱学习

	//(2)string& operator+=(const char c); 
	//将单个字符,拼接至原字符串结尾。
	str1 += ':';
	cout << "str1 = " << str1 << endl;	//我爱学习:

	//(3)string& operator+=(const string & str); 
	//将string类型的字符串,拼接至原字符串结尾。
	string str2 = "C++";
	str1 += str2;
	cout << "str1 = " << str1 << endl;	//我爱学习:C++

	//(4)string& append(const char* s); 
	//将const char* 类型的字符串,拼接至原字符串结尾。
	str1.append("/Python");
	cout << "str1 = " << str1 << endl;	//我爱学习:C++/Python

	//(5)string& append(const char* s, int n);
	//将const char* 类型的字符串s的前n个字符,拼接至原字符串结尾。
	str1.append("/JavaWeb", 5);
	cout << "str1 = " << str1 << endl;	//我爱学习:C++/Python/Java

	//(6)string& append(const string & s); 
	//将string类型的字符串,拼接至原字符串结尾。
	string str3 = "/MySQL";
	str1.append(str3);
	cout << "str1 = " << str1 << endl;	//我爱学习:C++/Python/Java/MySQL

	//(7)string& append(const string & s, int pos, int n);
	//将string类型的字符串s中自索引位置pos起的n个字符(长度为n的子串),拼接至原字符串结尾。
	string str4 = "Windows/Linux";
	str1.append(str4, 7, 6);
	cout << "str1 = " << str1 << endl;	//我爱学习:C++/Python/Java/MySQL/Linux

	return 0;
}

1.6 string查找【find()、rfind()】和替换【replace()】

(1)查找:通过成员函数find()rfind(),查找指定字符串是否存在。

find()rfind()区别find()从左往右查找;rfind()从右往左查找。

函数原型:
int find(const string& str, int pos = 0) const;【从左往右查找,第一次出现位置】
源字符串的指定索引位置pos(默认为0)起,查找string类型的目标字符串str第一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int find(const char* s, int pos = 0) const;【从左往右查找,第一次出现位置】
源字符串的指定索引位置pos(默认为0)起,查找const char*类型的目标字符串s第一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int find(const char* s, int pos, int n) const;【从左往右查找,第一次出现位置】
源字符串的指定索引位置pos起,查找const char*类型的目标字符串sn个字符第一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int find(const char c, int pos = 0) const;【从左往右查找,第一次出现位置】
源字符串的指定索引位置pos(默认为0)起,查找目标字符c第一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int rfind(const string& str, int pos = npos) const; 【从右往左查找,第一次出现位置】
源字符串的指定索引位置pos(默认为npos,即-1)起,查找string类型的目标字符串str最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

注1:npos是string类的静态常量(使用类名访问,即string::npos),表示字符串中不存在的位置,即结束位置,通常表示为-1
定义static const size_type npos = -1;
注2:判断源字符串src是否包含目标字符串dest的方法:
if (src.find(dest) != string::npos){...}

int rfind(const char* s, int pos = npos) const; 【从右往左查找,第一次出现位置】
源字符串的指定索引位置npos(默认为npos,即-1)起,查找const char*类型的目标字符串s最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int rfind(const char* s, int pos, int n) const;【从右往左查找,第一次出现位置】
源字符串的指定索引位置pos起,查找const char*类型的目标字符串sn个字符最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

int rfind(const char c, int pos = 0) const; 【从右往左查找,第一次出现位置】
源字符串的指定索引位置pos(默认为0)起,查找目标字符c最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1

示例:字符串查找

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

/* 字符串查找 */
void func() {
	string str1 = "abcdefgbc";
	string str2 = "bc";

	//find():从左往右查找,第1次出现位置
	int pos1 = str1.find(str2);
	if (pos1 != -1) {
		cout << "str1包含str2" << endl;
		cout << "第一次出现的索引位置为:" << pos1 << endl;		//1
	}
	else {
		cout << "str1不包含str2" << endl;
	}

	//rfind():从右往左查找,第1次出现位置(即从左往右查找时的【最后1次】出现位置)
	int pos2 = str1.rfind(str2);
	if (pos2 != -1) {
		cout << "str1包含str2" << endl;
		cout << "最后一次出现的索引位置为:" << pos2 << endl;		//7
	}
	else {
		cout << "str1不包含str2" << endl;
	}
}

(2)替换:在指定位置替换字符串。
函数原型:
string& replace(int pos, int n, const string& str);
源字符串指定索引位置pos起的连续n个字符,替换为string类型的整个目标字符串str
string& replace(int pos, int n, const char* s);
源字符串指定索引位置pos起的连续n个字符,替换为const char*类型的整个目标字符串s

示例:字符串替换

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

/* 字符串替换 */
void func2() {
	string str1 = "abcdefg";
	string str2 = "****";

	//replace():将源字符串指定索引位置pos起的连续n个字符,替换为整个目标字符串
	str1.replace(1, 2, str2);	//将str1的"bc"等2个字符,替换为整个str2
	cout << "替换后的str1 = " << str1 << endl;	//a****defg
}

1.7 string字符串比较【compare()】

作用:通过成员函数compare()比较两个字符串。主要用于判断两个字符串是否相等,即if(s1.compare(s2) == 0)

比较方式:逐个比较每个字符的ASCII码:
①相等时,继续比较后一个字符,直至完成所有字符的比较,全部相等则返回0
②不等时,大于则返回1;小于则返回-1

函数原型
(1)int compare(const string &s) const;:将源字符串与string类型的字符串进行比较。
(2)int compare(const char *s) const;:将源字符串与const char*类型的字符串进行比较。

示例:字符串比较

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

int main() {
	string str1 = "abc";
	string str2 = "acb";
	
	if (str1.compare(str2) == 0) {
		cout << "str1 等于 str2" << endl;
	}
	else if(str1.compare(str2) < 0) {
		cout << "str1 小于 str2" << endl;	//str1 小于 str2
	}
	else if (str1.compare(str2) > 0) {
		cout << "str1 大于 str2" << endl;
	}

	return 0;
}

1.8 string字符存取【operator[]、at()】

作用:通过重载赋值运算符operator[]成员函数at(int index),对字符串的单个字符进行(作为右值)或(作为左值)。

注:string类型字符串的底层实际为字符数组,可通过索引访问字符数组的字符元素。

字符存取的2种方式
(1)char& operator[](int index);:重载运算符[],读写指定索引位置index的字符
(2)char& at(int index);:成员函数,读写指定索引位置index的字符

示例:

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

int main() {
	/* 重载运算符[]读写字符 */
	string str1 = "abcd";
	cout << "修改前:str1 = " << str1 << endl;		//abcd
	//读取字符
	cout << "str1[1] = " << str1[1] << endl;		//b
	//写入字符
	str1[1] = 'a';
	cout << "修改后:str1 = " << str1 << endl;		//aacd


	/* 成员函数at()读写字符 */
	string str2 = "abcd";
	cout << "修改前:str2 = " << str2 << endl;		//abcd
	//读取字符
	cout << "str2.at(1) = " << str2.at(1) << endl;	//b
	//写入字符
	str2.at(1) = 'a';
	cout << "修改后:str1 = " << str2 << endl;		//aacd

	return 0;
}

1.9 string插入【insert()】和删除【erase()】

(1)字符串插入:使用成员函数insert(..)在原字符串中插入字符串或字符

函数原型
string& insert(int pos, const char* s);:在原字符串指定索引位置pos插入const char*类型的字符串。
string& insert(int pos, const string& str);:在原字符串指定索引位置pos插入string类型的字符串。
string& insert(int pos, int n, char c);:在原字符串指定索引位置pos插入n个字符。


(2)字符串删除:使用成员函数erase(..)在原字符串中删除若干个连续字符

函数原型
string& erase(int pos, int n = npos);:从原字符串的指定索引位置pos开始,连续删除n个字符。

示例:字符串的插入与删除

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

int main() {
	//insert():在原字符串指定索引位置pos前,插入字符或字符串
	string str = "abcd";
	str.insert(1, "***"); //在字符b前(索引位置1)插入***
	cout << "str = " << str << endl;	//a***bcd

	//erase():从原字符串的指定索引位置pos开始,连续删除n个字符
	str.erase(1, 3);	//从索引位置1开始,连续删除3个字符
	cout << "str = " << str << endl;	//abcd

	return 0;
}

1.10 string获取子串【substr()】

作用:通过成员函数substr(...),从原字符串中获取子字符串

函数原型
string substr(int pos = 0, int n = npos) const;
从原字符串的指定索引位置pos开始,截取连续n个字符作为子字符串并返回。

示例:截取子字符串

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

int main() {
	string str = "helloworld";
	//从索引位置5开始截取5个字符并返回子字符串
	string newStr = str.substr(5, 5);
	cout << "newStr = " << newStr << endl;	//world


	/* 实际应用场景 */
	string email = "NewsomTech@163.com";
	//获取@的索引位置(@前共有pos个字符)
	int pos = email.find("@");
	//从原字符串索引位置0,截取pos个字符
	string username = email.substr(0, pos);
	cout << "username = " << username << endl;	//NewsomTech

	return 0;
}

2 vector容器

2.1 vector基本概念

作用单端数组,仅可在容器的尾部插入或删除元素,与数组非常相似。
区别普通数组静态空间vector支持动态扩展容量机制。

动态扩展:并非在原有内存空间中扩展新空间,而是申请更大的内存空间,将原有数据拷贝至新的内存空间,并释放原有内存空间

vector容器的迭代器:支持随机访问的迭代器,可跳跃式访问容器元素。
vector容器
push_back()尾插法插入数据。
pop_back()尾删法删除数据。
front():获取vector容器的第1个元素
back():获取vector容器的最后1个元素
begin():获取起始迭代器,指向容器中的第1个元素
end():获取结束迭代器,指向容器中的最后1个元素下一个位置


2.2 vector构造函数

作用:创建vector容器。

注:使用vector容器时,需包含头文件#include <vector>

函数原型
(1)vector<T> v;:默认无参构造函数,采用类模板实现。
(2)vector(v.begin(), v.end());:拷贝容器对象v[begin(), end())区间左闭右开,包左不包右)的元素进行初始化。
(3)vector(n, elem);:有参构造函数,使用nelem元素进行初始化。
(4)vector(const vector &vec);:拷贝构造函数,使用已有vector对象初始化新的对象。

注:通常使用默认无参构造函数拷贝构造函数实例化vector容器对象。

示例:vector构造函数

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}

int main() {
	/* 1.默认无参构造函数 */
	vector<int> v1;

	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		v1.push_back(i);
	}
	//遍历
	printVector<int>(v1);	//0 1 2 3 4

	/* 2.有参构造,区间拷贝 */
	//左闭右开区间的元素
	vector<int> v2(v1.begin(), v1.end());
	printVector<int>(v2);	//0 1 2 3 4

	/* 3.n个elem元素赋值 */
	//5个整型元素6
	vector<int> v3(5, 6);
	printVector<int>(v3);	//6 6 6 6 6

	/* 4.拷贝构造函数 */
	vector<int> v4(v3);
	printVector<int>(v4);	//6 6 6 6 6

	return 0;
}

2.3 vector赋值操作【operator=、assign()】

作用:通过重载赋值运算符operator=成员函数assign(),对vector容器进行赋值。

函数原型
(1)vector& operator=(const vector &vec);:重载赋值运算符,使用目标vector容器,对当前vector容器赋值。
(2)assign(begin, end);:拷贝目标vector容器中[begin(), end())区间左闭右开,包左不包右)的元素,对当前vector容器赋值。
(3)assign(n, elem);:使用nelem元素,对当前vector容器赋值。

示例:vector赋值操作

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}


int main() {
	vector<int> v;

	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		v.push_back(i);
	}
	//遍历
	printVector<int>(v);	//0 1 2 3 4

	/* 1.重载运算符=赋值 */
	vector<int> v1 = v;
	printVector<int>(v1);	//0 1 2 3 4

	/* 2.assign()函数,区间拷贝 */
	//左闭右开区间的元素
	vector<int> v2;
	v2.assign(v.begin(), v.end());
	printVector<int>(v2);	//0 1 2 3 4

	/* 3.assign()函数,n个elem元素赋值 */
	vector<int> v3;
	//6个整型元素6
	v3.assign(6, 6);
	printVector<int>(v3);	//6 6 6 6 6 6

	return 0;
}

2.4 vector容量【capacity()】和大小【size()、resize()】

作用:操作vector容器的容量大小(即元素个数)。

注:容器的容量始终大于等于容器的大小(即元素个数),并支持动态扩展容量

函数原型
(1)empty();:判断容器是否为空。
(2)capacity();:获取容器的容量
(3)size();:获取容器的大小,即元素个数
(4)resize(int num);重新指定容器的长度为num
若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除
(5)resize(int num, elem);重新指定容器的长度为num
若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除

示例:vector容量和大小

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}

int main() {
	vector<int> v1;
	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		v1.push_back(i);
	}
	printVector<int>(v1);	//0 1 2 3 4

	//empty():判断容器是否为空
	cout << (v1.empty() ? "v1为空" : "v1不为空") << endl;	//v1不为空

	//capacity(); :获取容器的容量。
	cout << "v1的容量:" << v1.capacity() << endl;		//6(随机值)
	
	//size(); :获取容器的大小,即元素个数。
	cout << "v1的大小/元素个数:" << v1.size() << endl;	//5

	//resize(int num);:重新指定容器的长度为num
	//若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	v1.resize(10);			//长度变大时,使用默认值0填充
	printVector<int>(v1);	//0 1 2 3 4 0 0 0 0 0

	v1.resize(3);			//长度变小时,容器末尾超出新长度的元素被删除
	printVector<int>(v1);	//0 1 2

	//resize(int num, elem); :重新指定容器的长度为num。
	//若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	v1.resize(8, 6);		//长度变大时,使用指定值6填充
	printVector<int>(v1);	//0 1 2 6 6 6 6 6

	return 0;
}

2.5 vector插入【push_back()、insert()】和删除【pop_back()、erase()、clear()】

(1)vector容器插入元素:使用成员函数push_back(..)insert(..)向vector容器插入元素。

函数原型
push_back(elem);尾插法,向vector容器的尾部插入元素elem
insert(const_iterator pos, elem);迭代器指向位置pos插入元素elem
insert(const_iterator pos, int n, elem);迭代器指向位置pos插入n个元素elem


(2)vector容器删除元素:使用成员函数pop_back(..)erase(..)clear(..)删除vector容器中的元素。

函数原型
pop_back();尾删法,删除vector容器尾部的最后1个元素
erase(const_iterator pos);:删除迭代器指向位置的元素。
erase(const_iterator start, const_iterator end);:删除迭代器指向位置[start, end)区间的所有元素。
clear();:清空容器中的所有元素

注1:清空容器的所有元素,v.clear();等价于v.erase(v.begin(), v.end());
注2:插入元素insert(..)、删除元素erase(..)均需要迭代器对象作为参数。

示例:vector插入和删除元素

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}

int main() {
	vector<int> v;
	
	//插入元素:push_back()
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	printVector<int>(v);	//1 2 3 4 5

	//删除元素:pop_back()
	v.pop_back();
	printVector<int>(v);	//1 2 3 4

	//插入元素:insert(const_iterator pos, elem); 迭代器指向位置pos插入元素elem
	//在起始迭代器的指向位置插入元素10
	v.insert(v.begin(), 10);
	printVector<int>(v);	//10 1 2 3 4

	//插入元素:insert(const_iterator pos, int n, elem); 迭代器指向位置pos插入n个元素elem。
	//在起始迭代器的指向位置插入2个元素0
	v.insert(v.begin(), 2 , 0);
	printVector<int>(v);	//0 0 10 1 2 3 4

	//删除元素:erase(const_iterator pos); 删除迭代器指向位置的元素
	//删除起始迭代器的指向位置的元素
	v.erase(v.begin());	
	printVector<int>(v);	//0 10 1 2 3 4

	//删除元素:erase(const_iterator start, const_iterator end); 删除迭代器指向位置[start, end)区间的所有元素。
	//删除起始迭代器后一个位置 至 结束迭代器 之间区间的所有元素
	v.erase(++v.begin(), v.end());
	printVector<int>(v);	//0

	//清空元素:clear(); 清空容器中的所有元素。
	v.clear();
	printVector<int>(v);	//(空)
	
	return 0;
}

2.6 vector数据存取【operator[]、at()】

作用:通过重载赋值运算符operator[]成员函数at(int index),对vector容器的单个元素进行(作为右值)或(作为左值)。

函数原型
(1)operator[](int index);:重载运算符[],读写指定索引位置index的元素。
(2)at(int index);:成员函数,读写指定索引位置index的元素。
(3)front();:返回容器的第1个元素
(4)back();:返回容器的最后1个元素

访问/遍历vector容器元素的方式
迭代器
重载运算符[]
成员函数at()

示例:vector读写元素

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}


int main() {
	vector<int> v;
	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		v.push_back(i);
	}

	//遍历方式1:重载运算符
	for (int i = 0; i < v.size(); i++) {
		//重载运算符[]:读写指定索引位置index的元素。
		cout << v[i] << " ";		//0 1 2 3 4
	}
	cout << endl;

	//遍历方式2:成员函数at()
	for (int i = 0; i < v.size(); i++) {
		//重载运算符[]:读写指定索引位置index的元素。
		cout << v.at(i) << " ";		//0 1 2 3 4
	}
	cout << endl;
	
	//修改元素1:重载运算符
	v[4] *= 10;
	printVector<int>(v);	//0 1 2 3 40

	//修改元素2:成员函数at()
	v.at(0) = -1;
	printVector<int>(v);	//-1 1 2 3 40

	//获取第1个元素
	cout << "第1个元素:" << v.front() << endl;	//-1

	//获取最后1个元素
	cout << "最后1个元素:" << v.back() << endl;	//40

	return 0;
}

2.7 vector互换容器【swap()】

函数原型swap(vec);:将目标vector容器vec的元素与自身互换。

作用
(1)实现两个vector容器的元素互换


(2)可结合匿名对象拷贝构造函数,实现容器内存收缩,如:vector<int>(v).swap(v);
背景:当容量较大的vector容器,使用成员函数resize()将容器的大小(元素个数)调整至较小值时,vector容器实际容量不变,导致内存资源浪费
内存收缩步骤
vector<int>(v):调用拷贝构造函数创建vector类的匿名对象,将根据原vector容器对象v的大小(元素个数)分配一定的容量(较小值)。
vector<int>(v).swap(v);:原vector对象v和匿名对象实现容器互换,即指针指向的内存地址互换,匿名对象指向具有较大容量的内存空间。
匿名对象调用完毕后立即释放,对应的较大容量的内存空间被自动释放。
④原vector对象v现指向具有较小容量的内存空间,实现容器内存收缩
vector容器互换-收缩容器内存

示例:vector容器互换

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//打印vector元素的函数模板
template<typename T>
void printVector(vector<T>& v) {
	//for循环遍历
	for (vector<T>::iterator it = v.begin(); it != v.end(); it++) {
		cout << (*it) << " ";
	}
	cout << endl;
}

//vector容器互换
void func1() {
	vector<int> v1;
	for (int i = 0; i < 5; i++) {
		v1.push_back(i);
	}

	vector<int> v2;
	for (int i = 4; i >= 0; i--) {
		v2.push_back(i);
	}

	cout << "交换前v1容器:" << endl;
	printVector<int>(v1);	//0 1 2 3 4
	cout << "交换前v2容器:" << endl;
	printVector<int>(v2);	//4 3 2 1 0

	//容器互换
	v1.swap(v2);

	cout << "交换后v1容器:" << endl;
	printVector<int>(v1);	//4 3 2 1 0
	cout << "交换后v2容器:" << endl;
	printVector<int>(v2);	//0 1 2 3 4
}


//实际作用:vector容器内存收缩
void func2(){
	//现有容器v开始时具有较大容量和较多元素
	vector<int> v;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);
	}
	cout << "resize前..." << endl;
	cout << "v的容量:" << v.capacity() << endl;	//容量:138255
	cout << "v的大小:" << v.size() << endl;		//大小:100000

	/* resize() 重新指定大小,长度为5 */
	v.resize(5);

	//resize容器大小后,容量保持【较大值】不变,容器大小变为【较小值】
	//内存资源浪费
	cout << "resize后..." << endl;
	cout << "v的容量:" << v.capacity() << endl;	//容量:138255
	cout << "v的大小:" << v.size() << endl;		//大小:5
	
	/* 创建匿名对象,并进行容器互换 */
	vector<int>(v).swap(v);

	//容器互换后,原vector容器的容量和大小均变为【较小值】,实现容器内存收缩
	//匿名对象调用完毕后立即释放,指向的较大内存空间被释放
	cout << "容器互换后..." << endl;
	cout << "v的容量:" << v.capacity() << endl;	//容量:5
	cout << "v的大小:" << v.size() << endl;		//大小:5
}

int main() {
	//func1();
	func2();

	return 0;
}

2.8 vector预留空间【reserve()】

作用:当vector容器存储的数据量较大时,一开始可利用成员函数reserve()预留指定大小的内存空间,减少vector容器在动态扩展容量时的扩展次数,降低数据拷贝过程的资源消耗。

函数原型
reserve(int len);:容器预留len个元素长度的内存空间,但预留内存空间不进行初始化不可访问

注:reserve(...)reverse()需注意区分。
reserve(...)用于vector容器预留空间
reverse(...)用于反转容器的元素,如reverse(v.begin(), v.end());

示例:vector预留空间

#include <iostream>
using namespace std;
#include <vector>	//包含头文件

//vector预留空间
void func() {
	vector<int> v;

	/* 预留空间 */
	//v.reserve(10000);		//动态扩展7次
	//v.reserve(20000);		//动态扩展5次
	//v.reserve(50000);		//动态扩展3次
	v.reserve(100000);		//动态扩展1次

	//统计vector插入100000个元素时,动态扩展容量的总次数
	int count = 0;
	//初始化空指针,用于判断是否动态扩展
	int* p = NULL;

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

		//判断指向vector容器首元素的指针,记录的地址值是否发生改变
		//首地址发生改变 → vector容器已动态扩展
		if (p != &v[0]) {
			//更新指针的地址值
			p = &v[0];
			count++;
			//cout << "vector容器的首地址:" << p << endl;
		}
	}
	cout << "动态扩展容量的总次数:" << count << endl;
	//未预留空间时:动态扩展30次
	//预留10000个元素长度时:动态扩展7次
	//预留20000个元素长度时:动态扩展5次
	//预留50000个元素长度时:动态扩展3次
	//预留100000个元素长度时:动态扩展1次
}

int main() {
	func();

	return 0;
}

3 deque容器

3.1 deque容器基本概念

作用双端数组,可在容器的头部尾部插入或删除元素。

deque容器的迭代器:支持随机访问的迭代器,可跳跃式访问容器元素。

deque与vector区别
总结:deque头插、头删,查询/访问;vector头插、头删慢,查询/访问快。
(1)vector容器属于单端数组头部插入或删除元素的效率低,需要移动所有元素,且数据量越大,效率越低。
deque容器属于双端数组,支持头部插入或删除元素,效率高于vector容器。

(2)vector访问元素的速度快于deque
vector内部采用连续的线性空间
deque内部采用中央控制器数据缓冲区。当访问至当前缓冲区的末尾时,需通过中央控制器查找下一缓冲区的地址,再访问下一缓冲区的数据。


deque的内部工作原理(内部结构)
deque内部存在中央控制器记录与维护每段数据缓冲区的内存地址,缓冲区中存储真实数据,保证可从容器的头部与尾部插入或删除元素。
deque的内部结构,保证使用deque类似于访问连续的内存空间,但实际是不连续的内存空间。
deque内部结构


deque容器
push_front()头插法插入数据。
pop_front()头删法删除数据。
push_back()尾插法插入数据。
pop_back()尾删法删除数据。
front():获取deque容器的第1个元素
back():获取deque容器的最后1个元素
begin():获取起始迭代器,指向容器中的第1个元素
end():获取结束迭代器,指向容器中的最后1个元素下一个位置


3.2 deque构造函数

作用:创建deque容器。

注:使用deque容器时,需包含头文件#include <deque>

函数原型
(1)deque<T> deq;:默认无参构造函数,采用类模板实现。
(2)deque(deq.begin(), deq.end());:拷贝容器对象deq[begin(), end())区间左闭右开,包左不包右)的元素进行初始化。
(3)deque(n, elem);:有参构造函数,使用nelem元素进行初始化。
(4)deque(const deque &deq);:拷贝构造函数,使用已有deque对象初始化新的对象。

注:当遍历vectordeque容器的元素时:
若希望遍历过程中元素不被修改,可使用只读迭代器const_iterator
若将遍历功能封装成函数时,可将函数形参使用const修饰。
tip:当遍历函数的形参使用const修饰后,遍历访问时必须使用只读迭代器const_iterator

示例:deque构造函数

#include <iostream>
using namespace std;
#include <deque>	//包含头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T> &deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时必须使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	/* 1.默认无参构造函数 */
	deque<int> d1;

	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		d1.push_back(i);
	}
	//遍历
	printDeque<int>(d1);	//0 1 2 3 4

	/* 2.有参构造,区间拷贝 */
	//左闭右开区间的元素
	deque<int> d2(d1.begin(), d1.end());
	printDeque<int>(d2);	//0 1 2 3 4

	/* 3.n个elem元素赋值 */
	//5个整型元素6
	deque<int> v3(5, 6);
	printDeque<int>(v3);	//6 6 6 6 6

	/* 4.拷贝构造函数 */
	deque<int> v4(v3);
	printDeque<int>(v4);	//6 6 6 6 6

	return 0;
}

3.3 deque赋值操作【operator=、assign()】

作用:通过重载赋值运算符operator=成员函数assign(),对deque容器进行赋值。

函数原型
(1)deque& operator=(const deque &deq);:重载赋值运算符,使用目标deque容器,对当前deque容器赋值。
(2)assign(begin, end);:拷贝目标deque容器中[begin(), end())区间左闭右开,包左不包右)的元素,对当前deque容器赋值。
(3)assign(n, elem);:使用nelem元素,对当前deque容器赋值。

示例:deque赋值操作

#include <iostream>
using namespace std;
#include <deque>	//包含头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T>& deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	deque<int> deq;

	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		deq.push_back(i);
	}
	//遍历
	printDeque<int>(deq);	//0 1 2 3 4

	/* 1.重载运算符=赋值 */
	deque<int> d1 = deq;
	printDeque<int>(d1);	//0 1 2 3 4

	/* 2.assign()函数,区间拷贝 */
	//左闭右开区间的元素
	deque<int> d2;
	d2.assign(deq.begin(), deq.end());
	printDeque<int>(d2);	//0 1 2 3 4

	/* 3.assign()函数,n个elem元素赋值 */
	deque<int> d3;
	//5个整型元素6
	d3.assign(5, 6);
	printDeque<int>(d3);	//6 6 6 6 6

	return 0;
}

3.4 deque大小操作【size()、resize()】

作用:操作deque容器的大小(即元素个数)。

函数原型
(1)empty();:判断容器是否为空。
(2)size();:获取容器的大小,即元素个数
(3)resize(int num);重新指定容器的长度为num
若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除
(4)resize(int num, elem);重新指定容器的长度为num
若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除

注:deque容器不存在容量的概念,即不存在capacity()成员函数。可随时开辟缓冲区存储数据。

示例:deque容的大小操作

#include <iostream>
using namespace std;
#include <deque>	//包含头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T>& deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	deque<int> deq;
	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		deq.push_back(i);
	}
	printDeque<int>(deq);	//0 1 2 3 4

	//empty():判断容器是否为空
	cout << (deq.empty() ? "deq为空" : "deq不为空") << endl;	//deq不为空

	//size(); :获取容器的大小,即元素个数。
	cout << "deq的大小/元素个数:" << deq.size() << endl;	//5

	//resize(int num);:重新指定容器的长度为num
	//若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	deq.resize(10);			//长度变大时,使用默认值0填充
	printDeque<int>(deq);	//0 1 2 3 4 0 0 0 0 0

	deq.resize(3);			//长度变小时,容器末尾超出新长度的元素被删除
	printDeque<int>(deq);	//0 1 2

	//resize(int num, elem); :重新指定容器的长度为num。
	//若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	deq.resize(8, 6);		//长度变大时,使用指定值6填充
	printDeque<int>(deq);	//0 1 2 6 6 6 6 6

	return 0;
}

3.5 deque插入【push_front()、push_back()、insert()】和删除【pop_front()、pop_back()、erase()、clear()】

(1)deque容器插入元素:使用成员函数push_front()push_back(..)insert(..)向deque容器插入元素。

函数原型
push_front(elem);头插法,向deque容器的头部插入元素elem
push_back(elem);尾插法,向deque容器的尾部插入元素elem
insert(const_iterator pos, elem);迭代器指向位置pos插入元素elem
insert(const_iterator pos, int n, elem);迭代器指向位置pos插入n个元素elem
insert(const_iterator pos, const_iterator begin, const_iterator end);迭代器指向位置pos插入[begin(), end())区间左闭右开,包左不包右)的元素,无返回值。


(2)deque容器删除元素:使用成员函数pop_front()pop_back(..)erase(..)clear(..)删除deque容器中的元素。

函数原型
pop_front()头删法,删除deque容器头部的第1个元素
pop_back();尾删法,删除deque容器尾部的最后1个元素
erase(const_iterator pos);:删除迭代器指向位置的元素。
erase(const_iterator start, const_iterator end);:删除迭代器指向位置[start, end)区间的所有元素。
clear();:清空容器中的所有元素

注1:清空容器的所有元素,deq.clear();等价于deq.erase(deq.begin(), deq.end());
注2:插入元素insert(..)、删除元素erase(..)均需要迭代器对象作为参数。

示例:deque容器插入和删除元素

#include <iostream>
using namespace std;
#include <deque>	//包含头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T>& deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//deque两端插入/删除数据
void func1() {
	deque<int> deq;

	//尾插法
	deq.push_back(10);
	deq.push_back(20);

	//头插法
	deq.push_front(2);
	deq.push_front(1);
	printDeque(deq);	//1 2 10 20

	//尾删法
	deq.pop_back();
	printDeque(deq);	//1 2 10

	//头删法
	deq.pop_front();
	printDeque(deq);	//2 10

	//清空元素
	deq.clear();
	printDeque(deq);	//(空)
}

//deque指定位置插入/删除数据
void func2() {
	deque<int> deq;
	for (int i = 0; i < 5; i++) {
		deq.push_back(i);
	}
	printDeque(deq);	//0 1 2 3 4

	//指定位置插入单个元素-insert()
	deq.insert(deq.begin(), 10);
	printDeque(deq);	//10 0 1 2 3 4

	//指定位置删除单个元素-erase()
	deq.erase(deq.begin());
	printDeque(deq);	//0 1 2 3 4

	//指定位置插入多个元素-insert()
	deq.insert(deq.end(),3 ,10);
	printDeque(deq);	//0 1 2 3 4 10 10 10

	//指定位置插入区间元素-insert()
	deque<int> d;
	d.insert(d.begin(), 3, -1);
	printDeque(d);	//-1 -1 -1

	//在deq尾部插入d的[begin, end)区间元素
	deq.insert(deq.end(), d.begin(), d.end());
	printDeque(deq);	//0 1 2 3 4 10 10 10 -1 -1 -1

	//删除区间元素-erase
	deq.erase(deq.begin(), deq.end());
	printDeque(deq);	//(空)
}

int main() {
	//func1();
	func2();
}

3.6 deque数据存取【operator[]、at()】

作用:通过重载赋值运算符operator[]成员函数at(int index),对deque容器的单个元素进行(作为右值)或(作为左值)。

函数原型
(1)operator[](int index);:重载运算符[],读写指定索引位置index的元素。
(2)at(int index);:成员函数,读写指定索引位置index的元素。
(3)front();:返回容器的第1个元素
(4)back();:返回容器的最后1个元素

访问/遍历deque容器元素的方式
迭代器
重载运算符[]
成员函数at()

示例:deque读写元素

#include <iostream>
using namespace std;
#include <deque>	//包含头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T>& deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	deque<int> deq;
	//尾插法插入元素
	for (int i = 0; i < 5; i++) {
		deq.push_back(i);
	}

	//遍历方式1:重载运算符
	for (int i = 0; i < deq.size(); i++) {
		//重载运算符[]:读写指定索引位置index的元素。
		cout << deq[i] << " ";		//0 1 2 3 4
	}
	cout << endl;

	//遍历方式2:成员函数at()
	for (int i = 0; i < deq.size(); i++) {
		//重载运算符[]:读写指定索引位置index的元素。
		cout << deq.at(i) << " ";	//0 1 2 3 4
	}
	cout << endl;

	//修改元素1:重载运算符
	deq[4] *= 10;
	printDeque<int>(deq);	//0 1 2 3 40

	//修改元素2:成员函数at()
	deq.at(0) = -1;
	printDeque<int>(deq);	//-1 1 2 3 40

	//获取第1个元素
	cout << "第1个元素:" << deq.front() << endl;	//-1

	//获取最后1个元素
	cout << "最后1个元素:" << deq.back() << endl;	//40

	return 0;
}

3.7 deque排序【sort()】

作用:利用STL中的排序算法的全局函数sort(),实现对deque容器的元素排序。

算法sort(iterator begin, iterator end)
迭代器指向位置区间[start, end)的所有元素进行排序,默认升序排序
例:sort(deq.begin(), deq.end());

注1:使用sort()函数实现排序时,需包含标准算法头文件#include <algorithm>
注2:支持随机访问迭代器的容器(vectordeque),均可直接使用sort()全局函数实现排序。

STL不同容器的迭代器类型

容器种类迭代器类型
vector随机访问迭代器
deque随机访问迭代器
list双向迭代器
set / multiset双向迭代器
map / multimap双向迭代器
stack不支持迭代器
queue不支持迭代器
priority_queue不支持迭代器

示例:deque元素排序

#include <iostream>
using namespace std;
#include <deque>	//包含头文件
#include <algorithm>	//标准算法头文件

//函数模板:遍历deque容器的通用函数
template<typename T>
void printDeque(const deque<T>& deq) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	for (deque<T>::const_iterator it = deq.begin(); it != deq.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	deque<int> deq;

	deq.push_back(17);
	deq.push_back(9);
	deq.push_back(20);
	deq.push_front(8);
	deq.push_front(0);
	deq.push_front(2);

	cout << "排序前:" << endl;
	printDeque<int>(deq);	//2 0 8 17 9 20

	//排序
	sort(deq.begin(), deq.end());

	cout << "排序后:" << endl;
	printDeque<int>(deq);	//0 2 8 9 17 20

	return 0;
}

3.8 案例练习:评委打分

案例描述
10位评委分别对5位选手打分,去除最高分与最低分,剩余评分取平均值作为选手得分。

思路:vector容器存储选手对象,deque容器存储评分值。
①使用vector容器存储5名选手的对象;
②遍历vector容器,对每个选手对象,通过for循环将10位评委的得分,存储至deque容器
③sort算法对deque容器排序,并删除最低分和最高分;
④遍历deque容器,计算平均分。

注:针对不同需求使用合适的容器操作数据,可提升代码效率。

#include <iostream>
using namespace std;
#include <vector>
#include <deque>
#include <algorithm>	//sort()排序
#include <string>
#include <ctime>		//随机数种子

class Player {
public:
	string name;
	int avg;

	Player(string name, int avg) {
		this->name = name;
		this->avg = avg;
	}  
};

//1.添加选手信息
void addPlayers(vector<Player> &v) {
	string nameSeed = "ABCDE";

	for (int i = 0; i < 5; i++) {
		string name = "选手";
		name += nameSeed[i];	//拼接

		int avg = 0;

		//创建Player对象
		Player p(name, avg);

		//向vector添加Player对象
		v.push_back(p);
	}
}

//2.为选手打分
void setScore(vector<Player> &v) {
	//遍历vector容器
	for (vector<Player>::iterator it = v.begin(); it != v.end(); it++) {
		//创建deque容器,保存分数
		deque<int> deq;
		for (int i = 0; i < 10; i++) {
			//创建随机数(60~100)
			int score = rand() % 41 + 60;
			deq.push_back(score);
		}

		//查看得分信息
		cout << "姓名:" << it->name << ",分数:" << endl;
		for (deque<int>::iterator dit = deq.begin(); dit != deq.end(); dit++) {
			cout << *dit << " ";
		}
		cout << endl;

		//排序deque容器
		sort(deq.begin(), deq.end());

		//去掉最高分
		deq.pop_back();
		//去掉最低分
		deq.pop_front();

		/* 求平均分 */
		int sum = 0;
		for (deque<int>::iterator dit = deq.begin(); dit != deq.end(); dit++) {
			sum += *dit;
		}

		int avg = sum / deq.size();
		//成员属性赋值
		it->avg = avg;
	}
}

//3.打印得分信息
void showInfo(vector<Player> &v) {
	for (vector<Player>::iterator it = v.begin(); it != v.end(); it++) {
		cout << "姓名:" << it->name << ",得分:" << it->avg << endl;
	}
}

int main() {
	//创建随机数种子
	srand((unsigned int)time(NULL));

	//创建存储选手对象的vector容器
	vector<Player> v;

	//1.添加选手信息
	addPlayers(v);

	//2.为选手打分
	setScore(v);

	//3.打印得分信息
	showInfo(v);

	return 0;
}

4 stack容器

4.1 stack基本概念

特点:栈stack先进后出FILO,First In Last Out)的数据结构,只有栈顶一端可插入或删除数据。
stack容器
栈顶元素top(),栈中只有栈顶的元素可被外界访问和使用,故栈不允许遍历行为
入栈 / 压栈push(),向栈顶压入元素。
出栈 / 弹栈pop(),从栈顶弹出元素。


4.2 stack常用接口

注:使用stack容器时,需包含头文件#include <stack>

构造函数
stack<T> stk;:默认无参构造函数,采用类模板实现。
stack(const stack &stk);:拷贝构造函数,使用已有stack对象初始化新的对象。

赋值操作
stack& operator=(const stack &stk);:重载赋值运算符,使用目标stack容器,对当前stack容器赋值。

数据存取
push(elem);:向栈顶压入元素。
pop();:从栈顶弹出元素。
top();:返回栈顶元素。

大小操作
empty();:判断栈是否为空。
size();:返回栈的大小。

示例:栈的常用接口

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

int main() {
	//创建stack容器
	stack<int> s;

	//压栈
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);
	s.push(5);

	cout << "当前栈的大小:" << s.size() << endl;	//5

	while (!s.empty()) {
		cout << "当前栈顶元素:" << s.top() << endl;	//5→4→3→2→1

		//出栈
		s.pop();
	}
	cout << "当前栈的大小:" << s.size() << endl;	//0

	return 0;
}

5 queue容器

5.1 queue基本概念

特点:队列queue先进先出FIFO,First In First Out)的数据结构,包括两个出口,在队尾插入元素(入队),在队首删除元素(出队)。
queue容器
队头/队首front(),队列容器允许从队头移除元素
队尾back(),队列容器允许从队尾新增元素

注:队列中,只有队头队尾可被外界访问和使用,故队列不允许遍历行为

入队push(),向队尾新增元素。
出队pop(),从队头/队首移除元素。


5.2 queue常用接口

注:使用queue容器时,需包含头文件#include <queue>

构造函数
queue<T> que;:默认无参构造函数,采用类模板实现。
queue(const queue &que);:拷贝构造函数,使用已有queue对象初始化新的对象。

赋值操作
queue& operator=(const queue &que);:重载赋值运算符,使用目标queue容器,对当前queue容器赋值。

数据存取
push(elem);:向队尾添加元素。
pop();:从队首移除元素。
front();:返回队首元素(第1个元素)。
back();:返回队尾元素(最后1个元素)。

大小操作
empty();:判断队列是否为空。
size();:返回队列的大小/长度。

示例:队列queue的常用接口

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

class Person {
public:
	string name;
	int level;

	Person(string name, int level) {
		this->name = name;
		this->level = level;
	}
};

int main() {
	//创建队列
	queue<Person> q;

	Person p1("唐僧", 30);
	Person p2("孙悟空", 99);
	Person p3("猪悟能", 60);
	Person p4("沙悟净", 50);

	//入队
	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	cout << "当前队列大小:" << q.size() << endl;	//4
	
	//循环出队
	while (!q.empty()) {
		cout << "当前队首元素:" << q.front().name << endl;
		cout << "当前队尾元素:" << q.back().name << endl;
		cout << "***********" << endl;
	
		//出队
		q.pop();
	}

	cout << "当前队列大小:" << q.size() << endl;	//0

	return 0;
}

6 list容器

6.1 list基本概念

作用链表链式存储数据。STL中的链表是双向循环链表。

链表:链表list是物理存储单元上非连续的存储结构,数据元素的逻辑顺序通过链表中的指针链接实现。链表由一系列节点组成。

节点的组成
数据域:存储数据元素
指针域:存储下一个节点的地址(指针指向下一个节点)。


list容器的迭代器
链表采用不连续的存储结构,故链表list的迭代器只支持前移后移(不支持跳跃式访问),属于双向迭代器

注:list容器插入或删除元素时,不会导致原有迭代器失效
vector容器插入或删除元素时,会导致原有迭代器失效


list容器的数据结构
STL中的list容器,采用双向循环链表的数据结构:
第1个节点的前继节点为最后1个节点(第1个节点的prev指针指向最后1个节点);
最后1个节点的后继节点为第1个节点(最后1个节点的next指针指向第1个节点)。
list结构
push_front()头插法插入节点。
pop_front()头删法删除节点。
push_back()尾插法插入节点。
pop_back()尾删法删除节点。
front():获取list容器的第1个节点
back():获取list容器的最后1个节点
begin():获取起始迭代器,指向容器中的第1个节点
end():获取结束迭代器,指向容器中的最后1个节点下一个位置


list的优缺点
优点
①采用动态存储分配,不会造成内存浪费或溢出;
②链表,可在任意位置插入或删除元素,只需修改指针指向,无需移动大量元素。

缺点
①链表包括数据域和指针域,占用内存空间较大;
②元素访问/遍历速度较慢。

注1:链表增删快、查询慢;数组增删慢、查询快。
注2:listvector是C++ STL中最常用的容器,各有优缺点。


6.2 list构造函数

作用:创建list容器。

注:使用list容器时,需包含头文件#include <list>

函数原型
(1)list<T> lst;:默认无参构造函数,采用类模板实现。
(2)list(lst.begin(), lst.end());:拷贝容器对象lst[begin(), end())区间左闭右开,包左不包右)的元素进行初始化。
(3)list(n, elem);:有参构造函数,使用nelem元素进行初始化。
(4)list(const list&lst);:拷贝构造函数,使用已有list对象初始化新的对象。

示例:list构造函数

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


//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T> &lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//1.无参构造函数
	list<int> lst;
	lst.push_back(5);
	lst.push_back(7);
	lst.push_back(9);
	lst.push_front(3);
	lst.push_front(1);
	printList<int>(lst);	//1 3 5 7 9

	//2.区间构造
	list<int> l1(lst.begin(), lst.end());
	printList<int>(l1);		//1 3 5 7 9

	//3.拷贝构造
	list<int> l2(l1);
	printList<int>(l2);		//1 3 5 7 9

	//4.n个elem元素构造
	list<int> l3(5, 6);
	printList<int>(l3);		//6 6 6 6 6

	return 0;
}

6.3 list赋值【operator=、assign()】和交换【swap()】

赋值操作:通过重载赋值运算符operator=成员函数assign(),对list容器进行赋值。

函数原型
(1)list& operator=(const list &lst);:重载赋值运算符,使用目标list容器,对当前list容器赋值。
(2)assign(begin, end);:拷贝目标list容器中[begin(), end())区间左闭右开,包左不包右)的元素,对当前list容器赋值。
(3)assign(n, elem);:使用nelem元素,对当前list容器赋值。


交换操作:实现list容器的交换。
swap(lst);:将目标list容器lst与自身的元素互换。

示例:list赋值与交换

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


//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//无参构造函数
	list<int> lst;
	lst.push_back(5);
	lst.push_back(7);
	lst.push_back(9);
	lst.push_front(3);
	lst.push_front(1);
	printList<int>(lst);	//1 3 5 7 9

	//1.重载赋值运算符
	list<int> l1;
	l1 = lst;		
	printList<int>(l1);		//1 3 5 7 9

	//2.assign()赋值
	list<int> l2;
	l2.assign(lst.begin(), lst.end());
	printList<int>(l2);		//1 3 5 7 9

	//3.n个elem元素:assign(n, elem)
	list<int> l3;
	l3.assign(3, 6);
	printList<int>(l3);		//6 6 6

	/* 容器交换 */
	cout << "交换前l2:" << endl;
	printList<int>(l2);		//1 3 5 7 9
	cout << "交换前l3:" << endl;
	printList<int>(l3);		//6 6 6

	l3.swap(l2);

	cout << "交换后l2:" << endl;
	printList<int>(l2);		//6 6 6
	cout << "交换后l3:" << endl;
	printList<int>(l3);		//1 3 5 7 9

	return 0;
}

6.4 list大小操作【size()、resize()】

作用:操作list容器的大小(即元素个数)。

函数原型
(1)empty();:判断容器是否为空。
(2)size();:获取容器的大小,即元素个数
(3)resize(int num);重新指定容器的长度为num
若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除
(4)resize(int num, elem);重新指定容器的长度为num
若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除

注:list容器不存在容量的概念,即不存在capacity()成员函数。
list支持动态存储分配,通过维护指针域允许随时插入或删除节点。

示例

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

//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//无参构造函数
	list<int> lst;
	lst.push_back(5);
	lst.push_back(7);
	lst.push_back(9);
	lst.push_front(3);
	lst.push_front(1);
	printList<int>(lst);	//1 3 5 7 9

	//size():容器大小
	cout << "容器大小:" << lst.size() << endl;		//5

	//empty():判断容器是否为空
	cout << (lst.empty() ? "空" : "非空") << endl;	//非空

	//resize(int num);:重新指定容器的长度为num。
	//若容器变长,则以默认值0填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	lst.resize(10);
	printList<int>(lst);	//1 3 5 7 9 0 0 0 0 0

	lst.resize(3);
	printList<int>(lst);	//1 3 5

	//resize(int num, elem); :重新指定容器的长度为num。
	//若容器变长,则以指定值elem填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
	lst.resize(8, 6);
	printList<int>(lst);	//1 3 5 6 6 6 6 6

	return 0;
}

6.5 list插入【push_front()、push_back()、insert()】和删除【pop_front()、pop_back()、erase()、clear()、remove()】

(1)list容器插入元素:使用成员函数push_front()push_back(..)insert(..)向list容器插入元素。

函数原型
push_front(elem);头插法,向list容器的头部插入元素elem
push_back(elem);尾插法,向list容器的尾部插入元素elem
insert(const_iterator pos, elem);迭代器指向位置pos插入元素elem
insert(const_iterator pos, int n, elem);迭代器指向位置pos插入n个元素elem
insert(const_iterator pos, const_iterator begin, const_iterator end);迭代器指向位置pos插入[begin(), end())区间左闭右开,包左不包右)的元素,无返回值。


(2)list容器删除元素:使用成员函数pop_front()pop_back(..)erase(..)clear(..)删除list容器中的元素。

函数原型
pop_front()头删法,删除list容器头部的第1个元素
pop_back();尾删法,删除list容器尾部的最后1个元素
erase(const_iterator pos);:删除迭代器指向位置的元素。
erase(const_iterator start, const_iterator end);:删除迭代器指向位置[start, end)区间的所有元素。
clear();:清空容器中的所有元素
remove(elem);:删除容器中与指定值elem匹配的所有元素

注1:清空容器的所有元素,lst.clear();等价于lst.erase(lst.begin(), lst.end());
注2:插入元素insert(..)、删除元素erase(..)均需要迭代器对象作为参数。

示例:list容器插入与删除元素

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

//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	list<int> lst;
	//尾插
	lst.push_back(5);
	lst.push_back(7);
	lst.push_back(9);
	
	//头插
	lst.push_front(3);
	lst.push_front(1);
	printList<int>(lst);	//1 3 5 7 9

	//尾删
	lst.pop_back();
	printList<int>(lst);	//1 3 5 7

	//头删
	lst.pop_front();
	printList<int>(lst);	//3 5 7

	//insert()-插入单个元素
	//在第0个与第1个索引位置插入元素4
	lst.insert(++lst.begin(), 4);
	printList<int>(lst);	//3 4 5 7

	//erase()-删除单个元素
	//删除第1个索引位置的元素
	lst.erase(++lst.begin());
	printList<int>(lst);	//3 5 7

	//insert()-插入多个元素
	//在第0个索引位置前插入3个1
	lst.insert(lst.begin(), 3 , 0);
	printList<int>(lst);	//0 0 0 3 5 7


	list<int> l1;
	//insert()-插入区间元素
	l1.insert(l1.begin(), lst.begin(), lst.end());
	printList<int>(l1);		//0 0 0 3 5 7

	//remove()-删除指定值的元素
	l1.remove(0);
	printList<int>(l1);		//3 5 7

	//erase()-删除区间元素
	l1.erase(l1.begin(), l1.end());
	printList<int>(l1);		//(空)

	//remove()-清空元素
	lst.clear();
	printList<int>(lst);	//(空)

	return 0;
}

6.6 list数据存取【front()、back()】

作用:对list容器中数据进行存取。

注1:链表底层采用非连续线性的内存空间,且仅支持双向迭代器(只能前移和后移),无法使用索引跳跃式地访问元素。故不支持重载赋值运算符operator[]成员函数at(int index)跳跃式地访问指定索引位置元素。
注2:list容器支持双向迭代器,允许前移it--;和后移it++;
list容器不支持随机访问迭代器,不允许跳跃式移动it += 1;it -= 2;,否则编译器报错:没有与操作数匹配的 += 或 -= 运算符

函数原型
front();:返回容器的第1个元素
back();:返回容器的最后1个元素

示例

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

//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	list<int> lst;
	//尾插
	lst.push_back(1);
	lst.push_back(3);
	lst.push_back(5);
	lst.push_back(7);
	lst.push_back(9);
	printList<int>(lst);	//1 3 5 7 9

	cout << "第1个元素:" << lst.front() << endl;	//1
	cout << "最后1个元素:" << lst.back() << endl;	//9

	//修改元素
	lst.front() = 0;
	lst.back() = 100;
	printList<int>(lst);	//0 3 5 7 100


	//list容器支持双向迭代器,允许前移和后移
	list<int>::iterator it = lst.begin();
	it++;	//允许后移
	it--;	//允许前移

	//list容器不支持随机访问迭代器,不允许跳跃式移动
	//it += 1;	//报错:没有与操作数匹配的 += 运算符
	//it += 3;	//报错:没有与操作数匹配的 += 运算符
	//it -= 1;	//报错:没有与操作数匹配的 -= 运算符
	//it -= 3;	//报错:没有与操作数匹配的 -= 运算符

	return 0;
}

6.7 list反转【reverse()】和排序【sort()】

(1)list反转
reverse();:将容器中的元素反转。


(2)list排序
sort();:将容器中的数据进行排序,默认升序排序。

注1:链表排序的sort()函数是list容器的成员函数,而不是标准算法头文件<algorithm>全局函数
注2:可通过回调函数仿函数实现sort()函数按自定义规则排序。


回调函数-实现自定义排序

//回调函数
bool myCompare(int val1, int val2){
	//return val1 < val2;	//升序排序
	return val1 > val2;		//降序排序
}

//使用回调函数,按自定义规则排序
lst.sort(myCompare);

仿函数/函数对象-实现自定义排序

//函数对象/仿函数
class MyCompare {
public:
	//重载函数调用运算符()
	bool operator()(int val1, int val2) {
		//return val1 < val2;	//升序排序
		return val1 > val2;		//降序排序
	}
};

//使用仿函数/函数对象,按自定义规则排序
//MyCompare mc;
//lst.sort(mc);			//函数对象mc
lst.sort(MyCompare());	//匿名函数对象MyCompare()

示例:使用回调函数和仿函数,实现内置数据类型排序

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

//函数模板:遍历list容器的通用函数
template<typename T>
void printList(const list<T>& lst) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename list<T>::const_iterator it = lst.begin(); it != lst.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//反转
void func1() {
	list<int> lst;
	//尾插
	lst.push_back(5);
	lst.push_back(8);
	lst.push_back(3);
	lst.push_back(6);
	lst.push_back(1);

	cout << "反转前:" << endl;
	printList<int>(lst);	//5 8 3 6 1

	//反转
	lst.reverse();
	cout << "反转后:" << endl;
	printList<int>(lst);	//1 6 3 8 5
}

//回调函数-自定义排序规则
bool myCompare(int val1, int val2) {
	//return val1 < val2;	//升序排序
	return val1 > val2;	//降序排序
}

//函数对象/仿函数-自定义排序规则
class MyCompare {
public:
	//重载函数调用运算符()
	bool operator()(int val1, int val2) {
		//return val1 < val2;	//升序排序
		return val1 > val2;	//降序排序
	}
};


//排序
void func2() {
	list<int> lst;
	//尾插
	lst.push_back(5);
	lst.push_back(8);
	lst.push_back(3);
	lst.push_back(6);
	lst.push_back(1);

	cout << "排序前:" << endl;
	printList<int>(lst);	//5 8 3 6 1

	//默认升序排序
	lst.sort();
	cout << "升序排序后:" << endl;
	printList<int>(lst);	//1 3 5 6 8

	//降序排序:使用回调函数
	//lst.sort(myCompare);

	//降序排序:使用仿函数/函数对象
	//MyCompare mc;
	//lst.sort(mc);			//函数对象
	lst.sort(MyCompare());	//匿名函数对象

	cout << "降序排序后:" << endl;
	printList<int>(lst);	//8 6 5 3 1
}

int main() {
	//func1();
	func2();

	return 0;
}

6.8 案例练习:自定义数据类型的排序

注1:对于自定义数据类型,必须指定排序规则,否则编译器不清楚如何排序。
注2:高级排序是在排序规则的基础上额外制定一次或多次逻辑规则。

案例描述
对自定义数据类型Person进行排序,Person类包括姓名、成绩、年龄等成员属性。
排序规则:先按成绩降序排序,再按年龄升序排序。

示例:自定义数据类型Person的排序

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

class Person {
public:
	string name;
	int score;
	int age;

	Person(string name, int score, int age) {
		this->name = name;
		this->score = score;
		this->age = age;
	}
};

//排序规则的回调函数
bool myCompare1(Person p1, Person p2) {
	//先按照成绩socre降序排序
	if (p1.score == p2.score) {
		//成绩socre相同时,再按年龄age升序排序
		return p1.age < p2.age;
	}
	else {
		return p1.score > p2.score;
	}
}

//排序规则的回调函数:使用三目运算符简化
bool myCompare2(Person p1, Person p2) {
	//成绩不等时,按成绩降序排序
	//成绩相等时,按年龄升序排序
	return (p1.score == p2.score) ? (p1.age < p2.age) : (p1.score > p2.score);
}

int main() {
	list<Person> personList;

	//创建Person对象
	Person p1("普通青年", 60, 20);
	Person p2("聪明青年", 100, 20);
	Person p3("油腻中年", 60, 40);
	Person p4("勤奋青年", 80, 20);
	Person p5("摸鱼青年", 60, 30);
	Person p6("天才少年", 100, 15);

	//向list容器添加Person对象
	personList.push_back(p1);
	personList.push_back(p2);
	personList.push_back(p3);
	personList.push_back(p4);
	personList.push_back(p5);
	personList.push_back(p6);

	for (list<Person>::iterator it = personList.begin(); it != personList.end(); it++) {
		cout << "姓名:" << (*it).name << ", "
			<< "成绩:" << it->score << ", "
			<< "年龄:" << it->age << endl;
	}

	cout << "----------排序后-----------" << endl;

	//自定义排序:调用回调函数
	//personList.sort(myCompare1);
	personList.sort(myCompare2);

	for (list<Person>::iterator it = personList.begin(); it != personList.end(); it++) {
		cout << "姓名:" << (*it).name << ","
			<< "成绩:" << it->score << ","
			<< "年龄:" << it->age << endl;
	}
}

输出结果:

姓名:普通青年, 成绩:60, 年龄:20
姓名:聪明青年, 成绩:100, 年龄:20
姓名:油腻中年, 成绩:60, 年龄:40
姓名:勤奋青年, 成绩:80, 年龄:20
姓名:摸鱼青年, 成绩:60, 年龄:30
姓名:天才少年, 成绩:100, 年龄:15
----------排序后-----------
姓名:天才少年,成绩:100,年龄:15
姓名:聪明青年,成绩:100,年龄:20
姓名:勤奋青年,成绩:80,年龄:20
姓名:普通青年,成绩:60,年龄:20
姓名:摸鱼青年,成绩:60,年龄:30
姓名:油腻中年,成绩:60,年龄:40

7 set/multiset 容器

7.1 set基本概念

set / multiset属于关联式容器,底层结构采用红黑树实现。
特点:插入无序元素时,会自动完成排序。(输入无序序列,输出有序)

set和multiset的区别
set不允许容器中存在重复元素
multiset允许容器中存在重复元素


7.2 set构造函数和赋值【operator=】

作用:创建set容器及赋值。

注:使用set容器时,需包含头文件#include <set>

构造函数
set<T> st;:默认无参构造函数,采用类模板实现。
set(const set &st);:拷贝构造函数,使用已有set对象初始化新的对象。


赋值操作
set& operator=(const set &st);:重载赋值运算符,使用目标set容器,对当前set容器赋值。

示例:set构造函数和赋值

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

//函数模板:遍历set容器的通用函数
template<typename T>
void printSet(const set<T>& s) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename set<T>::const_iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//1.默认无参构造
	set<int> s;

	//插入元素
	s.insert(9);
	s.insert(1);
	s.insert(7);
	s.insert(3);
	s.insert(5);

	//自动排序
	printSet<int>(s);	//1	3 5 7 9

	//2.拷贝构造
	set<int> s1(s);
	printSet<int>(s1);	//1	3 5 7 9

	//3.赋值操作
	set<int> s2;
	s2 = s1;
	printSet<int>(s2);	//1	3 5 7 9

	return 0;
}

7.3 set大小【size()】和交换【swap()】

大小操作
size();:获取容器的大小,即元素个数
empty();:判断容器是否为空。

注:set集合不允许出现重复元素,故不支持resize()操作,避免指定长度变大时,填充默认值或指定值时插入重复元素。


交换操作
swap(st);:将目标set容器st与自身的元素互换。

示例

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

//函数模板:遍历set容器的通用函数
template<typename T>
void printSet(const set<T>& s) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename set<T>::const_iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//默认无参构造
	set<int> s1;

	//插入元素
	s1.insert(9);
	s1.insert(1);
	s1.insert(7);
	s1.insert(3);
	s1.insert(5);

	//empty() 判断是否为空
	cout << (s1.empty() ? "空" : "非空") << endl;		//非空
	//size() 大小
	cout << s1.size() << endl;	//5

	/* 交换操作 */
	set<int> s2;
	s2.insert(4);
	s2.insert(8);
	s2.insert(6);
	s2.insert(2);

	cout << "交换前:" << endl;
	printSet(s1);	//1 3 5 7 9
	printSet(s2);	//2 4 6 8

	//swap() 交换操作
	s1.swap(s2);

	cout << "交换后:" << endl;
	printSet(s1);	//2 4 6 8
	printSet(s2);	//1 3 5 7 9

	return 0;
}

7.4 set插入【insert()】和删除【erase()、clear()】

(1)插入元素:使用成员函数insert(..)向set容器插入元素。

函数原型
insert(elem);:向set容器插入元素elem


(2)删除元素:使用成员函数erase(..)clear(..)删除set容器中的元素。

函数原型
erase(const_iterator pos);:删除迭代器指向位置的元素。
erase(const_iterator start, const_iterator end);:删除迭代器指向位置[start, end)区间的所有元素。
erase(elem);:删除容器中值为elem元素。
clear();:清空容器中的所有元素

注:清空容器的所有元素,s.clear();等价于s.erase(s.begin(), s.end());

示例:set容器插入和删除元素

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

//函数模板:遍历set容器的通用函数
template<typename T>
void printSet(const set<T>& s) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename set<T>::const_iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

int main() {
	//默认无参构造
	set<int> s;

	//插入元素
	s.insert(9);
	s.insert(1);
	s.insert(7);
	s.insert(3);
	s.insert(5);
	printSet(s);	//1 3 5 7 9

	//删除单个元素
	s.erase(s.begin());
	printSet(s);	//3 5 7 9

	//删除指定值
	s.erase(5);
	printSet(s);	//3 7 9

	//清空元素
	s.erase(s.begin(), s.end());
	//s.clear();
	printSet(s);	//(空)

	return 0;
}

7.5 set查找【find()】和统计【count()】

查找元素
find(key);:查找目标值key是否存在。若存在,则返回该值对应元素的迭代器指向位置;若不存在,则返回结束迭代器 s.end()。

注:成员函数find(key);返回值为迭代器类型

//判断set容器是否存在某元素
int val = 5;

//查找元素,返回迭代器类型
set<int>::iterator pos = s.find(val);
//判断元素是否存在
if(pos != s.end()){
	cout << "查找到目标值:" << *pos << endl;
}

统计元素个数
count(key);:统计目标值key的元素个数。
set容器:元素不可重复,返回结果为01
multiset容器:元素可重复,返回结果为01n

示例:set查找元素和统计元素个数

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

//查找元素
void func1() {
	set<int> s;

	//插入元素
	s.insert(9);
	s.insert(1);
	s.insert(7);
	s.insert(3);
	s.insert(5);

	int key = 5;
	//find():查找元素,返回迭代器类型
	set<int>::iterator pos = s.find(key);
	
	//判断元素是否存在
	if (pos != s.end()) {
		cout << "查找到目标值:" << *pos << endl;
	}else{
		cout << "未找到目标值:" << endl;
	}
}

//统计元素个数-set容器
void func2() {
	set<int> s;

	//插入元素
	s.insert(9);
	s.insert(1);
	s.insert(7);
	s.insert(3);
	s.insert(5);
	s.insert(5);
	s.insert(5);

	//set容器:元素不可重复,返回结果为0或1。
	cout << s.count(5) << endl;		//1
}

//统计元素个数-multiset容器
void func3() {
	multiset<int> s;

	//插入元素
	s.insert(9);
	s.insert(1);
	s.insert(7);
	s.insert(3);
	s.insert(5);
	s.insert(5);
	s.insert(5);

	//multiset容器:元素可重复,返回结果为0、1或n。
	cout << s.count(5) << endl;		//3
}

int main() {
	//func1();
	//func2();
	func3();

	return 0;
}

7.6 set和multiset区别

set和multiset的区别
set:不可插入重复数据。
pair<iterator, bool> insert(value_type&& _Val);
set容器insert(elem)函数返回值类型为pair对组pair<iterator, bool>,会返回插入操作的结果,即不可以插入重复元素。

multiset:可插入重复数据。
iterator insert(value_type&& _Val);
multiset容器insert(elem)函数返回值类型为iterator,不会返回插入操作的结果,即可以插入重复元素。

示例

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

//set插入数据
void func1() {
	set<int> s;

	//pair<iterator, bool> insert(value_type && _Val);
	//set容器的insert()元素返回 对组类型<迭代器类型, 布尔类型>
	pair<set<int>::iterator, bool> pair1 = s.insert(1);	//第1次插入元素1
	if (pair1.second) {	//第2个参数:布尔类型
		cout << "数据1插入成功.." << endl;
	}
	else {
		cout << "数据1插入失败.." << endl;
	}

	pair<set<int>::iterator, bool> pair2 = s.insert(1);	//第2次插入元素1
	if (pair2.second) {	//第2个参数:布尔类型
		cout << "数据1再次插入成功.." << endl;
	}
	else {
		cout << "数据1再次插入失败.." << endl;
	}

	//数据1插入成功..
	//数据1再次插入失败..
}

//multiset插入数据
void func2() {
	multiset<int> ms;

	//iterator insert(value_type && _Val);
	//multiset容器的insert()元素返回 迭代器类型
	set<int>::iterator it1 = ms.insert(1);	//第1次插入元素1
	set<int>::iterator it2 = ms.insert(1);	//第2次插入元素1
	
	for (typename set<int>::const_iterator it = ms.begin(); it != ms.end(); it++) {
		cout << *it << " ";		//1 1(两次插入元素1均成功)
	}
}

void main() {
	//func1();
	func2();
}

7.7 pair对组创建

对组:成对出现的数据。使用对组可返回两个数据。

对组的创建方式
(1)有参构造函数:pair<type1, type2> p(value1, value2);
(2)make_pair()函数:pair<type1, type2> p = make_pair(value1, value2);

对组元素的访问方式
成员属性first:返回对组第1个元素的值;
成员属性second:返回对组第2个元素的值。

示例:对组的使用

#include <iostream>
using namespace std;

void main() {
	//创建对组-有参构造函数
	pair<string, int> p1("Tom", 16);
	cout << p1.first << endl;	//Tom
	cout << p1.second << endl;	//16

	//创建对组-make_pair函数
	pair<string, int> p2 = make_pair("Jerry", 18);
	cout << p2.first << endl;	//Jerry
	cout << p2.second << endl;	//18
}

7.8 set容器排序【仿函数operator()】

set容器默认升序排序,利用仿函数,通过重载小括号operator()插入元素之前指定排序规则,并在创建set容器对象时,向set容器的类模板参数列表传入指定排序规则。

/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
template<class T>
class MyCompare {
public:
	/* 重载仿函数() */
	//第1个小括号:重载小括号运算符
	//第2个小括号:operator()函数的形参列表的小括号
	//必须使用const修饰operator()函数
	//否则编译器报错:const MyCompare的表达式会丢失 const-volatile 限定符
	bool operator()(T val1, T val2) const {
		return val1 > val2;		//降序排序
		//return val1 < val2;	//升序排序
	}
};

/* 创建set容器对象时,向set容器的类模板参数列表传入指定排序规则MyCompare类型 */
//类模板参数列表需传入类型,使用包含仿函数的MyCompare类型
set<double, MyCompare<double>> s;

注:使用set容器对自定义数据类型排序时,必须指定排序规则。


示例1:set容器对内置数据类型排序

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

/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
template<class T>
class MyCompare {
public:
	/* 重载仿函数() */
	//第1个小括号:重载小括号运算符
	//第2个小括号:operator()函数的形参列表的小括号
	//必须使用const修饰operator()函数
	//否则编译器报错:const MyCompare的表达式会丢失 const-volatile 限定符
	bool operator()(T val1, T val2) const {
		return val1 > val2;		//降序排序
		//return val1 < val2;	//升序排序
	}
};

int main() {
	//创建set容器对象时,向set容器的类模板参数列表传入指定排序规则MyCompare类型
	//类模板参数列表需传入类型,使用包含仿函数的MyCompare类型
	set<double, MyCompare<double>> s;

	//插入元素
	s.insert(6.6);
	s.insert(3.3);
	s.insert(9.9);
	s.insert(1.1);
	s.insert(7.7);

	//遍历set容器
	for (set<double, MyCompare<double>>::iterator it = s.begin(); it != s.end(); it++) {
		cout << *it << "  ";		//9.9  7.7  6.6  3.3  1.1
	}
	cout << endl;

	return 0;
}


示例2:set容器对自定义数据类型排序

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

class Student {
public:
	string name;
	int score;

	Student(string name, int score) {
		this->name = name;
		this->score = score;
	}
};

/* 定义自定义排序规则的仿函数的类 */
class MyCompare {
public:
	/* 重载仿函数() */
	//必须使用const修饰operator()函数
	bool operator()(Student s1, Student s2) const {
		return s1.score > s2.score;		//升序排序
		//return s1.score < s2.score;	//降序排序
	}
};

int main() {
	//创建set容器对象时,向set容器的类模板参数列表传入指定排序规则MyCompare类型
	set<Student, MyCompare> s;

	//插入元素
	Student s1("Jack", 80);
	Student s2("Tom", 100);
	Student s3("Jerry", 60);
	Student s4("Lucy", 90);

	s.insert(s1);
	s.insert(s2);
	s.insert(s3);
	s.insert(s4);

	//遍历set容器
	for (set<Student, MyCompare>::iterator it = s.begin(); it != s.end(); it++) {
		cout << "姓名:" << (*it).name << ",成绩:" << it->score << endl;
	}
	//姓名:Tom,成绩:100
	//姓名:Lucy,成绩:90
	//姓名:Jack,成绩:80
	//姓名:Jerry,成绩:60

	return 0;
}

8 map/multimap容器

8.1 map基本概念

map/multimap属于关联式容器,底层结构采用红黑树实现。

特点
①map中的所有元素均为对组pair,即键值对
②对组pair中第1个元素为key(键值),起索引作用;第2个元素为value(实值)。
③所有元素均按照元素的键值key自动排序

优点:
高效,可根据key值快速查找到对应的value值。

map和multimap的区别
map不允许容器中存在重复key值元素;
multimap允许容器中存在重复key值元素。

注:实值value允许存在重复。


8.2 map构造函数和赋值【operator=】

作用:创建map容器及赋值。

注1:使用map容器时,需包含头文件#include <map>
注2:map中所有元素均成对出现,插入数据时需使用对组

构造函数
map<T1, T2> mp;:默认无参构造函数,采用类模板实现。
map(const map &mp);:拷贝构造函数,使用已有map对象初始化新的对象。


赋值操作
map& operator=(const map &mp);:重载赋值运算符,使用目标map容器,对当前map容器赋值。

示例:map构造函数和赋值

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

template<typename T1, typename T2>
void printMap(const map<T1, T2>& mp) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename map<T1, T2>::const_iterator it = mp.begin(); it != mp.end(); it++) {
		cout << "key = " << (*it).first << " , value = " << it->second << endl;
	}
	cout << endl;
}

int main() {
	//1.默认无参构造
	map<int, string> m;

	//插入元素,每个元素均为对组
	//使用对组pair的匿名对象
	m.insert(pair<int, string>(3, "Tom"));
	m.insert(pair<int, string>(2, "Jerry"));
	m.insert(pair<int, string>(4, "Jack"));
	m.insert(make_pair(1, "Lucy"));

	printMap<int, string>(m);
	//key = 1, value = Lucy
	//key = 2, value = Jerry
	//key = 3, value = Tom
	//key = 4, value = Jack

	//2.拷贝构造
	map<int, string> m2(m);
	printMap<int, string>(m2);

	//3.赋值操作
	map<int, string> m3;
	m3 = m;
	printMap<int, string>(m3);

	return 0;
}

8.3 map大小【size()】和交换【swap()】

大小操作
size();:获取map容器的大小,即键值对/对组的个数
empty();:判断容器是否为空。


交换操作
swap(mp);:将目标map容器mp与自身的元素互换。

示例

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

template<typename T1, typename T2>
void printMap(const map<T1, T2>& mp) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename map<T1, T2>::const_iterator it = mp.begin(); it != mp.end(); it++) {
		cout << "key = " << (*it).first << " , value = " << it->second << endl;
	}
	cout << endl;
}

int main() {
	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));

	//size():容器大小
	cout << "m1的大小:" << m1.size() << endl;	//3

	//empty():判断是否为空
	cout << (m1.empty() ? "空" : "非空") << endl;	//非空


	cout << "交换前:" << endl;
	map<int, int> m2;
	m2.insert(pair<int, int>(4, 40));
	m2.insert(pair<int, int>(5, 50));
	m2.insert(pair<int, int>(6, 60));
	printMap<int, int>(m1);
	printMap<int, int>(m2);

	//swap():容器互换
	m1.swap(m2);

	cout << "交换后:" << endl;
	printMap<int, int>(m1);
	printMap<int, int>(m2);

	return 0;
}

8.4 map插入【insert()】和删除【erase()、clear()】

(1)插入元素:使用成员函数insert(..)向map容器插入元素。

函数原型
insert(elem);:向map容器插入键值对/对组元素elem
mp.insert(pair<T1, T2>(v1, v2));:使用对组的匿名对象。
mp.insert(make_pair(v1, v2));:使用make_pair()函数,无需显式指定数据类型
mp.insert(map<T1, T2>::value_type(v1, v2));:使用map<T1, T2>::value_type()函数。【不建议】
mp[key] = val;:使用重载后的[]运算符插入元素。【不建议】

注:使用第④种方式插入元素时,若访问不存在的key值,则会使用默认值0自动创建<key, 0>的对组,不建议用于插入元素,但可用于根据已有键值key获取实值。


(2)删除元素:使用成员函数erase(..)clear(..)删除map容器中的元素。

函数原型
erase(const_iterator pos);:删除迭代器指向位置的元素。
erase(const_iterator start, const_iterator end);:删除迭代器指向位置[start, end)区间的所有元素。
erase(key);:删除容器中键值为key的元素。
clear();:清空容器中的所有元素

注:清空容器的所有元素,mp.clear();等价于mp.erase(mp.begin(), mp.end());

示例

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

template<typename T1, typename T2>
void printMap(const map<T1, T2>& mp) {	//形参使用const,避免被修改
	//形参使用const后,遍历时需使用只读迭代器const_iterator
	//使用typename关键字,防止编译器报错:C2760(语法错误,意外标记“标识符”)
	for (typename map<T1, T2>::const_iterator it = mp.begin(); it != mp.end(); it++) {
		cout << "key = " << (*it).first << " , value = " << it->second << endl;
	}
	cout << endl;
}

int main() {
	map<int, string> namelist;
	/* insert()插入键值对/对组 */
	//(1)mp.insert(pair<T1, T2>(v1, v2));:使用对组的匿名对象。
	namelist.insert(pair<int, string>(3, "Lucy"));
	
	//(2)mp.insert(make_pair(v1, v2));:使用make_pair()函数,无需显式指定数据类型。
	namelist.insert(make_pair(2, "Jerry"));

	//(3)mp.insert(map<T1, T2>::value_type(v1, v2));:使用map<T1, T2>::value_type()函数。【不建议】
	namelist.insert(map<int, string>::value_type(4, "Tom"));

	//(4)mp[key] = val;:使用重载后的[]运算符插入元素。【不建议】
	namelist[1] = "Jack";
	printMap<int, string>(namelist);
	//key = 1, value = Jack
	//key = 2, value = Jerry
	//key = 3, value = Lucy
	//key = 4, value = Tom

	/* 删除元素 */
	//erase(key)
	namelist.erase(4);
	printMap<int, string>(namelist);
	//key = 1, value = Jack
	//key = 2, value = Jerry
	//key = 3, value = Lucy

	//erase(pos)
	namelist.erase(namelist.begin());
	printMap<int, string>(namelist);
	//key = 2, value = Jerry
	//key = 3, value = Lucy

	//erase(begin, end)
	//clear()
	namelist.erase(namelist.begin(), namelist.end());
	//namelist.clear();
	printMap<int, string>(namelist);
	//(空)

	return 0;
}

8.5 map查找【find()】和统计【count()】

查找元素
find(key);:查找键值key是否存在。若存在,则返回该键值对元素的迭代器指向位置;若不存在,则返回结束迭代器 mp.end()。

注:成员函数find(key);返回值为迭代器类型

//判断map容器是否存在某键
int key = 5;

//查找元素,返回迭代器类型
map<int, int>::iterator pos = mp.find(key);
//判断元素是否存在
if (pos != mp.end()) {
	cout << "查找到元素key:" << pos->first 
			<< " ,value = " << (*pos).second << endl;
}

统计元素个数
count(key);:统计键值key的元素个数。
map容器:键key不可重复,返回结果为01
multimap容器:键key可重复,返回结果为01n

示例:map查找元素和统计元素个数

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

//map容器:key不可重复
void func1() {
	map<int, int> mp;

	//插入元素
	mp.insert(pair<int, int>(2, 20));
	mp.insert(pair<int, int>(1, 10));
	mp.insert(pair<int, int>(4, 40));
	mp.insert(pair<int, int>(3, 30));

	//find(key);:查找键值key是否存在。
	//若存在,则返回该键值对元素的迭代器指向位置;
	//若不存在,则返回结束迭代器 mp.end()。
	map<int, int>::iterator pos = mp.find(4);

	if (pos != mp.end()) {
		cout << "查找到元素key:" << pos->first
			<< " ,value = " << (*pos).second << endl;
	}
	else {
		cout << "未查找到元素" << endl;
	}
	//查找到元素key:4 ,value = 40


	//count(key);:统计键值key的元素个数。
	cout << mp.count(3) << endl;	//1
	cout << mp.count(5) << endl;	//0
}

//multimap容器:key可重复
void func2() {
	multimap<int, int> mmp;

	//插入元素
	mmp.insert(make_pair(2, 20));
	mmp.insert(make_pair(2, 200));
	mmp.insert(make_pair(2, 2000));
	mmp.insert(pair<int, int>(1, 10));
	mmp.insert(pair<int, int>(4, 40));
	mmp.insert(pair<int, int>(3, 30));

	//find(key);:查找键值key是否存在。
	//若存在,则返回该键值对元素的迭代器指向位置;
	//若不存在,则返回结束迭代器 mp.end()。
	map<int, int>::iterator pos = mmp.find(4);

	if (pos != mmp.end()) {
		cout << "查找到元素key:" << pos->first
			<< " ,value = " << (*pos).second << endl;
	}
	else {
		cout << "未查找到元素" << endl;
	}
	//查找到元素key:4 ,value = 40


	//count(key);:统计键值key的元素个数。
	cout << mmp.count(2) << endl;	//3
	cout << mmp.count(5) << endl;	//0
}

int main(){
	//func1();
	func2();
}

8.6 map容器排序【仿函数operator()】

map容器默认按键值key升序排序,利用仿函数,通过重载小括号operator()插入元素之前指定排序规则,并在创建map容器对象时,向map容器的类模板参数列表传入指定排序规则。

/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
template<class T>
class MyCompare {
public:
	/* 重载仿函数() */
	//第1个小括号:重载小括号运算符
	//第2个小括号:operator()函数的形参列表的小括号
	//必须使用const修饰operator()函数
	//否则编译器报错:const MyCompare的表达式会丢失 const-volatile 限定符
	bool operator()(T key1, T key2) const {
		return key1 > key2;		//降序排序
		//return key1 < key2;	//升序排序
	}
};

/* 创建map容器对象时,向map容器的类模板参数列表传入指定排序规则MyCompare类型 */
//类模板参数列表需传入类型,使用包含仿函数的MyCompare类型
map<double, int, MyCompare<double>> mp;

注:使用map容器对自定义数据类型排序时,必须指定排序规则。

示例:map容器对内置数据类型排序

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

/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
template<class T>
class MyCompare {
public:
	/* 重载仿函数() */
	//第1个小括号:重载小括号运算符
	//第2个小括号:operator()函数的形参列表的小括号
	//必须使用const修饰operator()函数
	//否则编译器报错:const MyCompare的表达式会丢失 const-volatile 限定符
	bool operator()(T key1, T key2) const {
		return key1 > key2;		//降序排序
		//return key1 < key2;	//升序排序
	}
};

int main() {
	//创建map容器对象时,向map容器的类模板参数列表传入指定排序规则MyCompare类型
	//类模板参数列表需传入类型,使用包含仿函数的MyCompare类型
	map<double, int, MyCompare<double>> mp;

	//插入元素
	mp.insert(pair<double, int>(2.2, 20));
	mp.insert(pair<double, int>(3.3, 30));
	mp.insert(pair<double, int>(1.1, 10));
	mp.insert(pair<double, int>(4.4, 40));

	for (map<double, int, MyCompare<double>>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << "key = " << (*it).first << " , value = " << it->second << endl;
	}
	//key = 4.4, value = 40
	//key = 3.3, value = 30
	//key = 2.2, value = 20
	//key = 1.1, value = 10
	
	return 0;
}

8.7 案例练习:员工分组

案例描述
10名员工(ABCDEFGHIJ)分配不同部门:
①员工信息:姓名、工资;部门:共3个部门。
②随机为10名员工分配部门和工资。
③通过multimap插入键值对信息<部门编号, 员工对象>。
④分部门显示员工信息。

思路
①vector容器存放员工对象。
②遍历vector容器,对员工随机分组。
③分组后,将键值对信息<部门编号, 员工对象>存放至multimap容器。
④分部门显示员工信息。

案例代码

#include <iostream>
using namespace std;
#include <vector>
#include <map>
#include <ctime>

#define CPP 1
#define JAVA 2
#define PYTHON 3

//员工类
class Employee {
public:
	string name;
	int salary;
};

//添加员工信息
void addEmployee(vector<Employee>& v) {
	string nameSeed = "ABCDEFGHIJ";

	for (int i = 0; i < 10; i++) {
		Employee emp;
		emp.name = "员工";
		emp.name += nameSeed[i];

		//工资赋随机值
		emp.salary = rand() % 10001 + 10000;	//10000~20000

		//将员工对象插入至vector容器
		v.push_back(emp);
	}
}

//分配员工部门
void setDepartment(vector<Employee> &v, multimap<int, Employee> &mp) {
	//遍历vector容器,将员工对象插入至map中
	for (vector<Employee>::iterator it = v.begin(); it != v.end(); it++) {
		//部门编号赋随机值
		int deptId = rand() % 3 + 1;	//1~3

		mp.insert(make_pair(deptId, *it));
	}
}

//分部门显示员工信息
void showEmpInfo(multimap<int, Employee>& mp) {
	cout << "C++开发部门:" << endl;
	
	//查询在容器中的起始位置
	multimap<int, Employee>::iterator pos = mp.find(CPP);
	//统计部门人数
	int count = mp.count(CPP);

	//遍历某个部门的全部员工
	int index = 0;
	for (; pos != mp.end() && index < count; pos++, index++) {
		cout << "员工姓名:" << pos->second.name
			<< ",工资:" << pos->second.salary << endl;
	}

	cout << "----------------------------" << endl;
	cout << "Java开发部门:" << endl;

	//查询在容器中的起始位置
	pos = mp.find(JAVA);
	//统计部门人数
	count = mp.count(JAVA);

	//遍历某个部门的全部员工
	index = 0;
	for (; pos != mp.end() && index < count; pos++, index++) {
		cout << "员工姓名:" << pos->second.name
			<< ",工资:" << pos->second.salary << endl;
	}

	cout << "----------------------------" << endl;
	cout << "Python开发部门:" << endl;

	//查询在容器中的起始位置
	pos = mp.find(PYTHON);
	//统计部门人数
	count = mp.count(PYTHON);

	//遍历某个部门的全部员工
	index = 0;
	for (; pos != mp.end() && index < count; pos++, index++) {
		cout << "员工姓名:" << pos->second.name
			<< ",工资:" << pos->second.salary << endl;
	}
}

int main() {
	//添加随机化种子
	srand((unsigned int)time(NULL));

	//1.添加员工信息
	vector<Employee> vec;
	addEmployee(vec);

	//2.员工分组
	multimap<int, Employee> mp;
	setDepartment(vec, mp);

	//3.分部门显示员工信息
	showEmpInfo(mp);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值