容器 | 名称 | 底层数据结构 | 特点 | 迭代器类型 | 容器类型 |
---|---|---|---|---|---|
string | 字符串 | (动态)字符数组 | 支持随机访问 动态扩展容量 |
随机访问迭代器 | 序列式容器 |
vector | 单端数组/ 动态数组 |
(动态)数组 | 支持随机访问 动态扩展容量 |
随机访问迭代器 | 序列式容器 |
deque | 双端数组/ 双端队列 |
中央控制器 多个数据缓冲区 |
支持随机访问 首尾快速插入和删除元素 |
随机访问迭代器 | 序列式容器 |
list | 链表 | 双向循环链表 | 快速插入和删除元素 | 双向迭代器 | 序列式容器 |
stack | 栈 | list或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:stack
、queue
和priority_queue
本质是适配器,是对容器的再封装(包装)。
优先队列priority_queue
的本质为大根堆或小根堆,插入或删除元素的时间复杂度为O(log n)。
string和char *的区别:
string
:C++风格的字符串;本质是类。string类内部封装char*
的成员属性,是char*型容器,底层维护C语言的字符串。
char *
:C风格的字符串;本质是指针。
特点:
(1)string类
的内部封装了许多用于操作字符串的成员方法,例如:查找find、拷贝copy、删除delete、替换replace、插入insert等。
(2)string类
管理char*
的内存分配时,无需担心复制越界、取值越界、内存释放(析构调用)等,由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
#include
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)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
作用:通过重载赋值运算符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
#include
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;
}
作用:通过重载加法赋值运算符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
#include
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)查找:通过成员函数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*类型的目标字符串s
的前n
个字符第一次出现的索引位置。查找到,则返回匹配处第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*类型的目标字符串s
的前n
个字符最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1
。
⑧int rfind(const char c, int pos = 0) const;
【从右往左查找,第一次出现位置】
从源字符串的指定索引位置pos
(默认为0)起,查找目标字符c
最后一次出现的索引位置。查找到,则返回匹配处第1个字符的索引位置;未查找到,则返回-1
。
示例:字符串查找
#include
#include
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
#include
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
}
作用:通过成员函数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
#include
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;
}
作用:通过重载赋值运算符operator[]
和成员函数at(int index)
,对字符串的单个字符进行读(作为右值)或写(作为左值)。
注:
string类型
字符串的底层实际为字符数组,可通过索引访问字符数组的字符元素。
字符存取的2种方式:
(1)char& operator[](int index);
:重载运算符[],读写指定索引位置index
的字符
(2)char& at(int index);
:成员函数,读写指定索引位置index
的字符
示例:
#include
#include
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)字符串插入:使用成员函数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
#include
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;
}
作用:通过成员函数substr(...)
,从原字符串中获取子字符串。
函数原型:
string substr(int pos = 0, int n = npos) const;
从原字符串的指定索引位置pos
开始,截取连续n
个字符作为子字符串并返回。
示例:截取子字符串
#include
#include
using namespace std;
int main() {
string str = "helloworld";
//从索引位置5开始截取5个字符并返回子字符串
string newStr = str.substr(5, 5);
cout << "newStr = " << newStr << endl; //world
/* 实际应用场景 */
string email = "[email protected]";
//获取@的索引位置(@前共有pos个字符)
int pos = email.find("@");
//从原字符串索引位置0,截取pos个字符
string username = email.substr(0, pos);
cout << "username = " << username << endl; //NewsomTech
return 0;
}
作用:单端数组
,仅可在容器的尾部插入或删除元素,与数组非常相似。
区别:普通数组
是静态空间;vector
支持动态扩展容量机制。
动态扩展:并非在原有内存空间中扩展新空间,而是申请更大的内存空间,将原有数据拷贝至新的内存空间,并释放原有内存空间。
vector容器的迭代器:支持随机访问的迭代器,可跳跃式访问容器元素。
push_back()
:尾插法插入数据。
pop_back()
:尾删法删除数据。
front()
:获取vector容器的第1个元素。
back()
:获取vector容器的最后1个元素。
begin()
:获取起始迭代器,指向容器中的第1个元素。
end()
:获取结束迭代器,指向容器中的最后1个元素的下一个位置。
作用:创建vector容器。
注:使用vector容器时,需包含头文件
#include
。
函数原型:
(1)vector
:默认无参构造函数,采用类模板实现。
(2)vector(v.begin(), v.end());
:拷贝容器对象v
的[begin(), end())
区间(左闭右开,包左不包右)的元素进行初始化。
(3)vector(n, elem);
:有参构造函数,使用n
个elem元素
进行初始化。
(4)vector(const vector &vec);
:拷贝构造函数,使用已有vector对象初始化新的对象。
注:通常使用默认无参构造函数和拷贝构造函数实例化vector容器对象。
示例:vector构造函数
#include
using namespace std;
#include //包含头文件
//打印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;
}
作用:通过重载赋值运算符operator=
和成员函数assign()
,对vector容器进行赋值。
函数原型:
(1)vector& operator=(const vector &vec);
:重载赋值运算符,使用目标vector容器,对当前vector容器赋值。
(2)assign(begin, end);
:拷贝目标vector容器中[begin(), end())
区间(左闭右开,包左不包右)的元素,对当前vector容器赋值。
(3)assign(n, elem);
:使用n
个elem元素
,对当前vector容器赋值。
示例:vector赋值操作
#include
using namespace std;
#include //包含头文件
//打印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;
}
作用:操作vector容器的容量和大小(即元素个数)。
注:容器的容量始终大于等于容器的大小(即元素个数),并支持动态扩展容量。
函数原型:
(1)empty();
:判断容器是否为空。
(2)capacity();
:获取容器的容量。
(3)size();
:获取容器的大小,即元素个数。
(4)resize(int num);
:重新指定容器的长度为num
。
若容器变长,则以默认值0
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
(5)resize(int num, elem);
:重新指定容器的长度为num
。
若容器变长,则以指定值elem
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
示例:vector容量和大小
#include
using namespace std;
#include //包含头文件
//打印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;
}
(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
using namespace std;
#include //包含头文件
//打印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;
}
作用:通过重载赋值运算符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
using namespace std;
#include //包含头文件
//打印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;
}
函数原型:swap(vec);
:将目标vector容器vec
的元素与自身互换。
作用:
(1)实现两个vector容器的元素互换。
(2)可结合匿名对象
和拷贝构造函数,实现容器内存收缩,如:vector
。
背景:当容量较大的vector容器,使用成员函数resize()
将容器的大小(元素个数)调整至较小值时,vector容器实际容量不变,导致内存资源浪费。
内存收缩步骤:
①vector
:调用拷贝构造函数创建vector类的匿名对象,将根据原vector容器对象v
的大小(元素个数)分配一定的容量(较小值)。
②vector
:原vector对象v
和匿名对象实现容器互换,即指针指向的内存地址互换,匿名对象指向具有较大容量的内存空间。
③匿名对象调用完毕后立即释放,对应的较大容量的内存空间被自动释放。
④原vector对象v
现指向具有较小容量的内存空间,实现容器内存收缩。
示例:vector容器互换
#include
using namespace std;
#include //包含头文件
//打印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;
}
作用:当vector容器存储的数据量较大时,一开始可利用成员函数reserve()
预留指定大小的内存空间,减少vector容器在动态扩展容量时的扩展次数,降低数据拷贝过程的资源消耗。
函数原型:
reserve(int len);
:容器预留len
个元素长度的内存空间,但预留内存空间不进行初始化,不可访问。
注:
reserve(...)
和reverse()
需注意区分。
reserve(...)
用于vector容器预留空间;
reverse(...)
用于反转容器的元素,如reverse(v.begin(), v.end());
。
示例:vector预留空间
#include
using namespace std;
#include //包含头文件
//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;
}
作用:双端数组
,可在容器的头部和尾部插入或删除元素。
deque容器的迭代器:支持随机访问的迭代器,可跳跃式访问容器元素。
deque与vector区别:
总结:deque头插、头删快,查询/访问慢;vector头插、头删慢,查询/访问快。
(1)vector
容器属于单端数组
,头部插入或删除元素的效率低,需要移动所有元素,且数据量越大,效率越低。
deque
容器属于双端数组
,支持头部插入或删除元素,效率高于vector容器。
(2)vector
访问元素的速度快于deque
。
vector
内部采用连续的线性空间;
deque
内部采用中央控制器与数据缓冲区。当访问至当前缓冲区的末尾时,需通过中央控制器查找下一缓冲区的地址,再访问下一缓冲区的数据。
deque的内部工作原理(内部结构):
deque内部存在中央控制器
,记录与维护每段数据缓冲区
的内存地址,缓冲区中存储真实数据,保证可从容器的头部与尾部插入或删除元素。
deque的内部结构,保证使用deque类似于访问连续的内存空间,但实际是不连续的内存空间。
push_front()
:头插法插入数据。
pop_front()
:头删法删除数据。
push_back()
:尾插法插入数据。
pop_back()
:尾删法删除数据。
front()
:获取deque容器的第1个元素。
back()
:获取deque容器的最后1个元素。
begin()
:获取起始迭代器,指向容器中的第1个元素。
end()
:获取结束迭代器,指向容器中的最后1个元素的下一个位置。
作用:创建deque容器。
注:使用deque容器时,需包含头文件
#include
。
函数原型:
(1)deque
:默认无参构造函数,采用类模板实现。
(2)deque(deq.begin(), deq.end());
:拷贝容器对象deq
的[begin(), end())
区间(左闭右开,包左不包右)的元素进行初始化。
(3)deque(n, elem);
:有参构造函数,使用n
个elem元素
进行初始化。
(4)deque(const deque &deq);
:拷贝构造函数,使用已有deque对象初始化新的对象。
注:当遍历
vector
或deque
容器的元素时:
若希望遍历过程中元素不被修改,可使用只读迭代器const_iterator
;
若将遍历功能封装成函数时,可将函数形参使用const
修饰。
tip:当遍历函数的形参使用const
修饰后,遍历访问时必须使用只读迭代器const_iterator
。
示例:deque构造函数
#include
using namespace std;
#include //包含头文件
//函数模板:遍历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;
}
作用:通过重载赋值运算符operator=
和成员函数assign()
,对deque容器进行赋值。
函数原型:
(1)deque& operator=(const deque &deq);
:重载赋值运算符,使用目标deque容器,对当前deque容器赋值。
(2)assign(begin, end);
:拷贝目标deque容器中[begin(), end())
区间(左闭右开,包左不包右)的元素,对当前deque容器赋值。
(3)assign(n, elem);
:使用n
个elem元素
,对当前deque容器赋值。
示例:deque赋值操作
#include
using namespace std;
#include //包含头文件
//函数模板:遍历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;
}
作用:操作deque容器的大小(即元素个数)。
函数原型:
(1)empty();
:判断容器是否为空。
(2)size();
:获取容器的大小,即元素个数。
(3)resize(int num);
:重新指定容器的长度为num
。
若容器变长,则以默认值0
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
(4)resize(int num, elem);
:重新指定容器的长度为num
。
若容器变长,则以指定值elem
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
注:deque容器不存在容量的概念,即不存在
capacity()
成员函数。可随时开辟缓冲区存储数据。
示例:deque容的大小操作
#include
using namespace std;
#include //包含头文件
//函数模板:遍历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;
}
(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
using namespace std;
#include //包含头文件
//函数模板:遍历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();
}
作用:通过重载赋值运算符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
using namespace std;
#include //包含头文件
//函数模板:遍历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;
}
作用:利用STL中的排序算法的全局函数sort()
,实现对deque容器的元素排序。
算法:sort(iterator begin, iterator end)
对迭代器指向位置区间[start, end)
的所有元素进行排序,默认升序排序。
例:sort(deq.begin(), deq.end());
注1:使用
sort()
函数实现排序时,需包含标准算法头文件#include
。
注2:支持随机访问迭代器的容器(vector
、deque
),均可直接使用sort()
全局函数实现排序。
STL不同容器的迭代器类型:
容器种类 | 迭代器类型 |
---|---|
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set / multiset | 双向迭代器 |
map / multimap | 双向迭代器 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
示例:deque元素排序
#include
using namespace std;
#include //包含头文件
#include //标准算法头文件
//函数模板:遍历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;
}
案例描述:
10位评委分别对5位选手打分,去除最高分与最低分,剩余评分取平均值作为选手得分。
思路:vector容器存储选手对象,deque容器存储评分值。
①使用vector容器存储5名选手的对象;
②遍历vector容器,对每个选手对象,通过for循环将10位评委的得分,存储至deque容器;
③sort算法对deque容器排序,并删除最低分和最高分;
④遍历deque容器,计算平均分。
注:针对不同需求使用合适的容器操作数据,可提升代码效率。
#include
using namespace std;
#include
#include
#include //sort()排序
#include
#include //随机数种子
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;
}
特点:栈stack
是先进后出(FILO
,First In Last Out)的数据结构,只有栈顶一端可插入或删除数据。
栈顶元素:top()
,栈中只有栈顶的元素可被外界访问和使用,故栈不允许遍历行为。
入栈 / 压栈:push()
,向栈顶压入元素。
出栈 / 弹栈:pop()
,从栈顶弹出元素。
注:使用stack容器时,需包含头文件
#include
。
构造函数:
①stack
:默认无参构造函数,采用类模板实现。
②stack(const stack &stk);
:拷贝构造函数,使用已有stack对象初始化新的对象。
赋值操作:
stack& operator=(const stack &stk);
:重载赋值运算符,使用目标stack容器,对当前stack容器赋值。
数据存取:
push(elem);
:向栈顶压入元素。
pop();
:从栈顶弹出元素。
top();
:返回栈顶元素。
大小操作:
empty();
:判断栈是否为空。
size();
:返回栈的大小。
示例:栈的常用接口
#include
using namespace std;
#include
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;
}
特点:队列queue
是先进先出(FIFO
,First In First Out)的数据结构,包括两个出口,在队尾插入元素(入队
),在队首删除元素(出队
)。
队头/队首:front()
,队列容器允许从队头移除元素。
队尾:back()
,队列容器允许从队尾新增元素。
注:队列中,只有队头和队尾可被外界访问和使用,故队列不允许遍历行为。
入队:push()
,向队尾新增元素。
出队:pop()
,从队头/队首移除元素。
注:使用queue容器时,需包含头文件
#include
。
构造函数:
①queue
:默认无参构造函数,采用类模板实现。
②queue(const queue &que);
:拷贝构造函数,使用已有queue对象初始化新的对象。
赋值操作:
queue& operator=(const queue &que);
:重载赋值运算符,使用目标queue容器,对当前queue容器赋值。
数据存取:
push(elem);
:向队尾添加元素。
pop();
:从队首移除元素。
front();
:返回队首元素(第1个元素)。
back();
:返回队尾元素(最后1个元素)。
大小操作:
empty();
:判断队列是否为空。
size();
:返回队列的大小/长度。
示例:队列queue的常用接口
#include
using namespace std;
#include
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;
}
作用:链表
,链式存储数据。STL中的链表是双向循环链表。
链表:链表list
是物理存储单元上非连续的存储结构,数据元素的逻辑顺序通过链表中的指针链接实现。链表由一系列节点组成。
节点的组成:
①数据域:存储数据元素;
②指针域:存储下一个节点的地址(指针指向下一个节点)。
list容器的迭代器:
链表采用不连续的存储结构,故链表list
的迭代器只支持前移和后移(不支持跳跃式访问),属于双向迭代器。
注:
list
容器插入或删除元素时,不会导致原有迭代器失效;
vector
容器插入或删除元素时,会导致原有迭代器失效。
list容器的数据结构:
STL中的list
容器,采用双向循环链表的数据结构:
第1个节点的前继节点为最后1个节点(第1个节点的prev指针
指向最后1个节点);
最后1个节点的后继节点为第1个节点(最后1个节点的next指针
指向第1个节点)。
push_front()
:头插法插入节点。
pop_front()
:头删法删除节点。
push_back()
:尾插法插入节点。
pop_back()
:尾删法删除节点。
front()
:获取list容器的第1个节点。
back()
:获取list容器的最后1个节点。
begin()
:获取起始迭代器,指向容器中的第1个节点。
end()
:获取结束迭代器,指向容器中的最后1个节点的下一个位置。
list的优缺点:
优点:
①采用动态存储分配,不会造成内存浪费或溢出;
②链表,可在任意位置插入或删除元素,只需修改指针指向,无需移动大量元素。
缺点:
①链表包括数据域和指针域,占用内存空间较大;
②元素访问/遍历速度较慢。
注1:
链表
增删快、查询慢;数组
增删慢、查询快。
注2:list
和vector
是C++ STL中最常用的容器,各有优缺点。
作用:创建list容器。
注:使用list容器时,需包含头文件
#include
。
函数原型:
(1)list
:默认无参构造函数,采用类模板实现。
(2)list(lst.begin(), lst.end());
:拷贝容器对象lst
的[begin(), end())
区间(左闭右开,包左不包右)的元素进行初始化。
(3)list(n, elem);
:有参构造函数,使用n
个elem元素
进行初始化。
(4)list(const list&lst);
:拷贝构造函数,使用已有list对象初始化新的对象。
示例:list构造函数
#include
using namespace std;
#include
//函数模板:遍历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;
}
赋值操作:通过重载赋值运算符operator=
和成员函数assign()
,对list容器进行赋值。
函数原型:
(1)list& operator=(const list &lst);
:重载赋值运算符,使用目标list容器,对当前list容器赋值。
(2)assign(begin, end);
:拷贝目标list容器中[begin(), end())
区间(左闭右开,包左不包右)的元素,对当前list容器赋值。
(3)assign(n, elem);
:使用n
个elem元素
,对当前list容器赋值。
交换操作:实现list容器的交换。
swap(lst);
:将目标list容器lst
与自身的元素互换。
示例:list赋值与交换
#include
using namespace std;
#include
//函数模板:遍历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;
}
作用:操作list容器的大小(即元素个数)。
函数原型:
(1)empty();
:判断容器是否为空。
(2)size();
:获取容器的大小,即元素个数。
(3)resize(int num);
:重新指定容器的长度为num
。
若容器变长,则以默认值0
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
(4)resize(int num, elem);
:重新指定容器的长度为num
。
若容器变长,则以指定值elem
填充新位置;若容器变短,则容器末尾超出新长度的元素被删除。
注:list容器不存在容量的概念,即不存在
capacity()
成员函数。
list支持动态存储分配,通过维护指针域允许随时插入或删除节点。
示例:
#include
using namespace std;
#include
//函数模板:遍历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;
}
(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
using namespace std;
#include
//函数模板:遍历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;
}
作用:对list容器中数据进行存取。
注1:链表底层采用非连续线性的内存空间,且仅支持双向迭代器(只能前移和后移),无法使用
索引
跳跃式地访问元素。故不支持重载赋值运算符operator[]
和成员函数at(int index)
跳跃式地访问指定索引位置元素。
注2:list容器支持双向迭代器,允许前移it--;
和后移it++;
;
list容器不支持随机访问迭代器,不允许跳跃式移动it += 1;
或it -= 2;
,否则编译器报错:没有与操作数匹配的 += 或 -= 运算符
。
函数原型:
front();
:返回容器的第1个元素。
back();
:返回容器的最后1个元素。
示例:
#include
using namespace std;
#include
//函数模板:遍历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;
}
(1)list反转
reverse();
:将容器中的元素反转。
(2)list排序
sort();
:将容器中的数据进行排序,默认升序排序。
注1:链表排序的
sort()
函数是list容器的成员函数,而不是标准算法头文件的全局函数。
注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
using namespace std;
#include
//函数模板:遍历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;
}
注1:对于自定义数据类型,必须指定排序规则,否则编译器不清楚如何排序。
注2:高级排序是在排序规则的基础上额外制定一次或多次逻辑规则。
案例描述:
对自定义数据类型Person进行排序,Person类包括姓名、成绩、年龄等成员属性。
排序规则:先按成绩降序排序,再按年龄升序排序。
示例:自定义数据类型Person的排序
#include
using namespace std;
#include
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
set / multiset
属于关联式容器,底层结构采用红黑树实现。
特点:插入无序元素时,会自动完成排序。(输入无序序列,输出有序)
set和multiset的区别:
set
:不允许容器中存在重复元素;
multiset
:允许容器中存在重复元素。
作用:创建set容器及赋值。
注:使用set容器时,需包含头文件
#include
。
构造函数:
①set
:默认无参构造函数,采用类模板实现。
②set(const set &st);
:拷贝构造函数,使用已有set对象初始化新的对象。
赋值操作:
set& operator=(const set &st);
:重载赋值运算符,使用目标set容器,对当前set容器赋值。
示例:set构造函数和赋值
#include
using namespace std;
#include
//函数模板:遍历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;
}
大小操作:
size();
:获取容器的大小,即元素个数。
empty();
:判断容器是否为空。
注:set集合不允许出现重复元素,故不支持
resize()
操作,避免指定长度变大时,填充默认值或指定值时插入重复元素。
交换操作:
swap(st);
:将目标set容器st
与自身的元素互换。
示例:
#include
using namespace std;
#include
//函数模板:遍历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;
}
(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
using namespace std;
#include
//函数模板:遍历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;
}
查找元素:
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容器
:元素不可重复,返回结果为0
或1
。
②multiset容器
:元素可重复,返回结果为0
、1
或n
。
示例:set查找元素和统计元素个数
#include
using namespace std;
#include
//查找元素
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;
}
set和multiset的区别:
set
:不可插入重复数据。
pair
set容器insert(elem)
函数返回值类型为pair对组pair
,会返回插入操作的结果,即不可以插入重复元素。
multiset
:可插入重复数据。
iterator insert(value_type&& _Val);
multiset容器insert(elem)
函数返回值类型为iterator
,不会返回插入操作的结果,即可以插入重复元素。
示例:
#include
using namespace std;
#include
//set插入数据
void func1() {
set<int> s;
//pair 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();
}
对组:成对出现的数据。使用对组可返回两个数据。
对组的创建方式:
(1)有参构造函数:pair
(2)make_pair()函数:pair
对组元素的访问方式:
成员属性first
:返回对组第1个元素的值;
成员属性second
:返回对组第2个元素的值。
示例:对组的使用
#include
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
}
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
using namespace std;
#include
/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
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
using namespace std;
#include
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;
}
map/multimap
属于关联式容器,底层结构采用红黑树实现。
特点:
①map中的所有元素均为对组pair
,即键值对。
②对组pair
中第1个元素为key
(键值),起索引作用;第2个元素为value
(实值)。
③所有元素均按照元素的键值key
自动排序。
优点:
高效,可根据key
值快速查找到对应的value
值。
map和multimap的区别:
map
:不允许容器中存在重复的key
值元素;
multimap
:允许容器中存在重复的key
值元素。
注:实值
value
允许存在重复。
作用:创建map容器及赋值。
注1:使用map容器时,需包含头文件
#include
。
注2:map中所有元素均成对出现,插入数据时需使用对组。
构造函数:
①map
:默认无参构造函数,采用类模板实现。
②map(const map &mp);
:拷贝构造函数,使用已有map对象初始化新的对象。
赋值操作:
map& operator=(const map &mp);
:重载赋值运算符,使用目标map容器,对当前map容器赋值。
示例:map构造函数和赋值
#include
using namespace std;
#include
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;
}
大小操作:
size();
:获取map容器的大小,即键值对/对组的个数。
empty();
:判断容器是否为空。
交换操作:
swap(mp);
:将目标map容器mp
与自身的元素互换。
示例:
#include
using namespace std;
#include
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;
}
(1)插入元素:使用成员函数insert(..)
向map容器插入元素。
函数原型:
insert(elem);
:向map容器插入键值对/对组元素elem
。
①mp.insert(pair
:使用对组的匿名对象。
②mp.insert(make_pair(v1, v2));
:使用make_pair()
函数,无需显式指定数据类型。
③mp.insert(map
:使用map
函数。【不建议】
④mp[key] = val;
:使用重载后的[]
运算符插入元素。【不建议】
注:使用第④种方式插入元素时,若访问不存在的
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
using namespace std;
#include
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(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::value_type(v1, v2));:使用map::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;
}
查找元素:
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
不可重复,返回结果为0
或1
。
②multimap容器
:键key
可重复,返回结果为0
、1
或n
。
示例:map查找元素和统计元素个数
#include
using namespace std;
#include
//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();
}
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
using namespace std;
#include
/* 定义自定义排序规则的仿函数的类 */
//使用类模板增强通用性
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;
}
案例描述:
10名员工(ABCDEFGHIJ)分配不同部门:
①员工信息:姓名、工资;部门:共3个部门。
②随机为10名员工分配部门和工资。
③通过multimap插入键值对信息<部门编号, 员工对象>。
④分部门显示员工信息。
思路:
①vector容器存放员工对象。
②遍历vector容器,对员工随机分组。
③分组后,将键值对信息<部门编号, 员工对象>存放至multimap容器。
④分部门显示员工信息。
案例代码:
#include
using namespace std;
#include
#include
#include
#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;
}