目录
一 STL简要介绍
STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据,从实现角度来看,STL容器是一种class template。
算法:各种常用的算法,如sort、find、copy、for_each。从实现的角度来看,STL算法是一种function tempalte.
迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* , operator-> , operator++,operator–等指针相关操作予以重载的class template. 所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器。
仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte.
STL六大组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数。
C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
C++ 标准模板库的核心主要是以下三个组件:
组件 |
描述 |
容器(Containers) |
容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。 |
算法(Algorithms) |
算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。 |
迭代器(iterators) |
迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。 |
这三个组件都带有丰富的预定义函数,帮助我们通过简单的方式处理复杂的任务。
STL的优点
- STL 是 C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
- STL 的一个重要特性是将数据和操作分离。数据由容器类别加以管理,操作则由可定制的算法定义。迭代器在两者之间充当“粘合剂”,以使算法可以和容器交互运作。
- 程序员可以不用思考 STL 具体的实现过程,只要能够熟练使用 STL 就 OK 了。这样他们就可以把精力放在程序开发的别的方面。
- STL 具有高可重用性,高性能,高移植性,跨平台的优点。
- 高可重用性:STL 中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
- 高性能:如 map 可以高效地从十万条记录里面查找出指定的记录,因为 map 是采用红黑树的变体实现的。
- 高移植性:如在项目 A 上用 STL 编写的模块,可以直接移植到项目 B 上。
STL包含的头文件
在 C++ 标准中STL共有 13 个头文件:
头文件名称 |
功能描述 |
适用场景 |
关联的迭代器与算法 |
<list> |
链表 |
高频率删除、增加、修改 |
正向、逆向、顺序 |
<deque> |
双队列 |
仅在开头或结尾添加、,删除元素 |
/ |
<queue> |
单队列 |
仅在开头添加元素,结尾删除元素 |
/ |
<stack> |
栈 |
只能一端放入或删除元素 |
/ |
<set> |
集合 |
所有 |
正向、逆向、顺序 |
<map> |
映射 |
所有 |
/ |
<iterator> |
迭代器 |
所有 |
正向、逆向、顺序 |
<vector> |
可变长度集合容器 |
所有 |
正向、逆向、顺序 |
<functional> |
仿函数 |
所有 |
/ |
<algorithm> |
算法 |
所有 |
/ |
<numeric> |
数学计算 |
所有 |
/ |
<utility> |
键值对 |
所有 |
/ |
<memory> |
内存 |
所有 |
/ |
关于开源的定义原则
开源,并不仅仅是公开源代码。 开源软件的发布必须遵守以下标准:
1. 自由发布
任何组织都可以把一个或多个不同的开源软件作为自己程序的一部分,来进行出售或分发。开源软件的许可权中,不能对此要求支付版权费或其它费用。
[注] 自由发布限制了程序商因谋求短期营利,而放弃某些长期收益的作法。
2. 源代码
发布的程序必须包含源代码。如果产品的发布中没有源代码,那么必须提供一个免费的,容易获得其源代码的方式,比如Internet的免费下载, 以便于其更好的传播与复制。 源代码必须以一种易于程序开发人员开发的方式发布。 一切混淆源代码的作法都是不允许。以一种中间产物的形式(比如,预处理指令,转译程序)发布其源代码也是不允许的。
[注] 清晰的源代码促使人们修改它,促进了开源软件的升级、进化。
3. 派行作品
开源软件必须许可人们对其进行修改的权利,和对它产生派生作品的权利。允许人们以原作品同样的许可权,发布它们修改后的或派生出的作品。
[注] 提供易于修改的源代码,还需要赋予人们修改的权利,才能让开源软件快速变革。
4. 作者源代码的完整性
当开源软件使用者发布其修改的产品时,开源软件作者可以限制人们对其原作品的发布。人们不可以发布原作品, 除非许可权中允许补丁程序可以与原作品一起发布, 以便于其它使用者安装和使用这些补可作品。许可权限中可以要求派生的作品以不同于原作品的名字或版本号发布。
[注] 促进修改开源软件是件好事,但需要让使用者知道,谁该负责这个软件。原作者和维护者都应该拥有提供帮助和保护其名誉的权利。
补丁 + 源码的发布方式,可以确保开源作品易于获得稳定版,同时确保非官方的修改可以被人们使用,并且这些修改可以被人们很容易地识别出来。
5. 不得歧视某些人或团队
开源软件的许可协议中,不能存在对任何人或团队歧视的条款。
[注] 为了最大化的利益,任何人或团队都有平等的权利为开源事业做出贡献。
6. 不得歧视某些行业
开源软件的许可协议中,不得约束人们对其在某些行业的使用或开发。例如,不可以阻止人们把它用于商业,或遗传学上的研究。
[注] 防止开源软件的许可权中有阻止其用于商业行为的陷阱。我们想要商业开发人员加入开源事业中来。
7. 发布条款
所有对开源软件进行重新发布的软件,都会被该开源软件中所附的发布条款约束,但不可以再有另个的条款约束。
[注] 此条款的目的是防止因某些未公布的条款,而终止某个开源的发布。
8. 发布条款不能针对某个产品
开源软件中所附的权限,与它发布在某个产品中无关。如果从发布的开源程序中提取出一部分程序,进行使用或发布,同样受到原作品中发布条款的约束。
[注] 此条款是阻止另一种开源软件许可权的陷阱。
9. 许可权不能约束其它软件
许可权不能约束与其一同发布的其它软件。例如,许可权中不能强调与其一同发布的其它软件,也必须发布为开源软件。
[注] 开源软件中的许可权仅可以约束自己。
GPL兼容此条例。使用GPL库的软件,它需要遵守GPL许可,但它使用到的其它第三方库,可以独立出来,并不需遵守GPL许可。
10. 许可权必须技术中立
许可权中不可以间接或直接地鼓励某些个别的技术。
关于STL的迭代器
迭代器iterator主要用于遍历容器中的元素的,常用的迭代器按功能强弱分为输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。
输入迭代器:把输入流作为操作对象
输出迭代器:把输出流作为操作对象
前向迭代器:假设 p 是一个前向迭代器,则 p 支持 ++p,p++,*p 操作,还可以被复制或赋值,可以用 == 和 != 运算符进行比较。此外,两个正向迭代器可以互相赋值。
双向迭代器:双向迭代器具有正向迭代器的全部功能,除此之外,假设 p 是一个双向迭代器,则还可以进行 --p 或者 p-- 操作(即一次向后移动一个位置)。
随机访问迭代器:具有双向迭代器的全部功能,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较
表 1 不同容器的迭代器 |
|
容器 |
对应的迭代器类型 |
array |
随机访问迭代器 |
vector |
随机访问迭代器 |
deque |
随机访问迭代器 |
list |
双向迭代器 |
set / multiset |
双向迭代器 |
map / multimap |
双向迭代器 |
forward_list |
前向迭代器 |
unordered_map / unordered_multimap |
前向迭代器 |
unordered_set / unordered_multiset |
前向迭代器 |
stack |
不支持迭代器 |
queue |
不支持迭代器 |
迭代器定义方式 |
具体格式 |
正向迭代器 |
容器类名::iterator 迭代器名; |
常量正向迭代器 |
容器类名::const_iterator 迭代器名; |
反向迭代器 |
容器类名::reverse_iterator 迭代器名; |
常量反向迭代器 |
容器类名::const_reverse_iterator 迭代器名; |
二 各组件使用实例
- 容器
容器是模板类的集合,封装了一系列组织数据的方法(数据结构)。
常用的数据结构:数组(array) , 链表(list), tree(树),栈(stack), 队列(queue), 集合(set),映射表(map), 根据数据在容器中的排列特性,这些数据分为序列式容器和关联式容器两种。
容器种类 |
功能 |
序列容器 |
主要包括 vector 向量容器、list 列表容器以及 deque 双端队列容器。之所以被称为序列容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置,元素就会位于什么位置。 |
排序容器 |
包括 set 集合容器、multiset多重集合容器、map映射容器以及 multimap 多重映射容器。排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置。所以关联容器在查找时具有非常好的性能。 |
哈希容器 |
C++ 11 新加入 4 种关联式容器,分别是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函数确定。 |
- 序列式容器
强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。
序列式容器包括array<T,N>(数组容器)、 vector 向量容器、list 列表容器以及 deque 双端队列容器;
array<T,N>(数组容器):表示可以存储 N 个 T 类型的元素,是 C++ 本身提供的一种容器。此类容器一旦建立,其长度就是固定不变的,这意味着不能增加或删除元素,只能改变某个元素的值;
实例代码:
#include <iostream>
#include <array> //数组模板头文件
using namespace std;
int main() {
/*array 容器的大小是固定的,无法动态的扩展或收缩,这也就意味着,
在使用该容器的过程无法借由增加或移除元素而改变其大小,它只允许访问或者替换存储的元素。*/
//直接使用元素初始化数组
array<int, 5> arrays{12, 23, 44, 56, 77}; //也可以写为:array<int,5> arrays{}; 后续在赋值元素
//遍历数组元素
for (auto i = arrays.begin(); i < arrays.end(); i++) {
cout << "arrays:" << *i << ",";
}
cout << endl;
//先申明数组容器,之后在初始化
array<int, 10> values{}; //默认会全部初始化为0
//给数组赋值
for (int i = 0; i < values.size(); ++i) { //size返回数组的元素个数
values.at(i) = i + 10 * 2; //at返回指定元素的引用,如果元素位置越界则抛出异常。
}
//判断数组是否为空,不为空才开始遍历
if (!values.empty()) {
//遍历元素: 使用指针运算遍历数组元素
auto p = values.data(); //data返回第一个元素的指针
for (int i = 0; i < values.size(); ++i) {
cout << "vlaues:" << *(p + i) << ",";
}
cout << endl;
}
//输出数组指定位置的元素
cout << get<3>(values) << endl;
//数组数组的首元素与尾元素
cout << values.front() << "," << values.back() << endl;
//替换数组中的每一个元素为相同的元素,比如全部置为0
values.fill(0); //把values数组的元素全部置为0
for (int i = 0; i < values.size(); ++i) {
cout << values.at(i) << ","; //遍历所有位置的元素
}
cout << endl;
//申明两个相同长度大小的数组,然后交换两个数组的所有元素
array<int, 2> array1, array2;
int *p1, *p2;
p1 = array1.data(); //指向第一个数组元素的地址
p2 = array2.data(); //指向第二个数组元素的地址
//给两个数组同时赋值
for (int i = 0; i < array1.size(); ++i) {
array1.at(i) = i; //其实也可写为array1[i]=i; 使用at的好处是编译器会做数组越界检查。
array2.at(i) = i * 6 + 12;
}
//数组元素未交换前遍历数组
for (int i = 0; i < array1.size(); ++i) {
cout << *(p1 + i) << ",";
}
cout << endl;
for (int i = 0; i < array2.size(); ++i) {
cout << *(p2 + i) << ",";
}
cout << endl;
cout << "===========================交换数组元素之后==========" << endl;
//交换数组元素
array1.swap(array2);
//数组元素交换后遍历数组
for (int i = 0; i < array1.size(); ++i) {
cout << *(p1 + i) << ",";
}
cout << endl;
for (int i = 0; i < array2.size(); ++i) {
cout << *(p2 + i) << ",";
}
cout << endl;
/*
begin()/end() 和 cbegin()/cend()
array 容器模板类中的 begin() 和 end() 成员函数返回的都是正向迭代器,它们分别指向「首元素」和「尾元素+1」 的位置。
主要作用是实现初始化容器或者遍历容器中元素的操作。
cbegin() 和 cend() 成员函数返回的是 const 类型的正向迭代器,可以用来遍历容器内的元素,也可以访问元素,但是不能对所存储的元素进行修改。
rbegin()/rend() 和 crbegin()/crend()
可以分别得到指向最一个元素和第一个元素前一个位置的随机访问迭代器,又称它们为反向迭代器
需要注意的是,在使用反向迭代器进行 ++ 或 -- 运算时,++ 指的是迭代器向左移动一位,-- 指的是迭代器向右移动一位
,即这两个运算符的功能也“互换”了。
crbegin()/crend() 组合和 rbegin()/crend() 组合的功能唯一的区别在于,前者返回的迭代器为 const 类型,
即不能用来修改容器中的元素,除此之外在使用上和后者完全相同。
*/
//比较简便的遍历方式: for each
for (auto v: values) {
cout << "for each to values: " << v <<",";
}
return 0;
}
代码clone地址: [email protected]:XiaoWang_csdn/cpp_19_array.git
vector<T>(向量容器):用来存放 T 类型的元素,是一个长度可变的序列容器,即在存储空间不足时,会自动申请更多的内存。使用此容器,在尾部增加或删除元素的效率最高(时间复杂度为 O(1) 常数阶),在其它位置插入或删除元素效率较差(时间复杂度为 O(n) 线性阶,其中 n 为容器中元素的个数);
代码示范:
#include <iostream>
#include <vector> //导入vecto容器头文件
using namespace std;
int main() {
//申明rector容器变量,名称叫arrays
vector<int> arrays;
//在容器尾部插入元素
arrays.push_back(23); //底层是先创建元素,然后在拷贝元素到尾部。
arrays.push_back(54);
arrays.emplace_back(34); //c++11新增,和上面的功能相似,但是底层是直接创建这个元素
//在开头位置插入一个元素
arrays.insert(arrays.begin(), 35);
arrays.emplace(arrays.begin(),12); //在开头处插入一个元素12,这种方式每次只能插入一个元素,但是效率非常高。
//返回容器的大小
cout << "size: " << arrays.size() << endl;
//给容器增加容量
arrays.reserve(100);
//返回当前容量
cout << "arrays.reserve(100),size: " << arrays.capacity() << endl;
//把当前的容量调整当前实际元素大小
arrays.shrink_to_fit();
//返回当前容量
cout << "arrays.shrink_to_fit(),size: " << arrays.capacity() << endl;
//访问指定的元素
cout << arrays.at(0) << "," << arrays[1] << endl;
//遍历容器
for (auto i = arrays.begin(); i < arrays.end(); ++i) {
cout << "arrays:" << *i << ",";
}
cout << endl;
//另外几种初始化vector的方式
vector<int> vect(20, 0); //初始容量为20个,并且把每个元素都初始化为0,
for (auto i = vect.begin(); i < vect.end(); ++i) {
cout << "vect: " << *i << ",";
}
cout << "剩余元素个数:" << vect.size() << endl;
//移除部分元素: 从第0+5 至 20-3的元素,所以最后还剩余8个元素
vect.erase(vect.begin() + 5, vect.end() - 3);
for (auto i = vect.begin(); i < vect.end(); ++i) {
cout << "vect: " << *i << ",";
}
cout << "剩余元素个数:" << vect.size() << endl;
cout << vect.data() << endl;
//注意事项: 每当对rect扩容之后,rect的对象地址有可能发生变更,原来的对象可能会失效,在实际开发中需要留意
vect.reserve(20);
cout << vect.data() << endl;
//删除元素
vect.pop_back(); //删除一个尾部的元素,容器的容量不变
std::cout << "Hello, World!" << std::endl;
return 0;
}
代码clone地址:[email protected]:XiaoWang_csdn/cpp_20_vector.git
deque<T>(双端队列容器):和 vector 非常相似,区别在于使用该容器不仅尾部插入和删除元素高效,在头部插入或删除元素也同样高效,时间复杂度都是 O(1) 常数阶,但是在容器中某一位置处插入或删除元素,时间复杂度为 O(n) 线性阶;
代码实例:
#include <iostream>
#include "deque" //引入队列容器头文件
using namespace std;
int main() {
//建立容器对象
deque<int> a1; //没有任何元素
deque<int> a2(10); //有10个元素,没有指定默认值,则编译器自动把每个元素的值都指定为0
deque<int> a3(10, 1);//有10个元素,每个元素的初始值均为0
//给容器添加元素
a1.push_back(1); //向尾部添加元素
a2.push_back(2);
a3.push_back(3);
a1.push_front(2);//向头部添加元素
a2.push_front(3);
a3.push_front(4);
a1.pop_front(); //移除头部元素
a2.pop_front();
a3.pop_front();
a1.pop_back(); //移除尾部元素
a2.pop_back();
a3.pop_back();
//在头部和尾部添加元素 :这种方式和上面的push与pop类似
a1.emplace_front(12);
a1.emplace_back(34);
//检查队列容器是否为空
cout << (a1.empty() ? "队列为空" : "队列非空") << endl;
cout << (a2.empty() ? "队列为空" : "队列非空") << endl;
cout << (a3.empty() ? "队列为空" : "队列非空") << endl;
//获取队列容器的元素个数和容器大小
cout << "a1元素个数为:" << a1.size() << endl;
cout << "a2元素个数为:" << a2.size() << endl;
cout << "a3元素个数为:" << a3.size() << endl;
//给队列批量新增元素
for (int i = 0; i < 10; ++i) {
a1.push_back(i);
}
cout << "a1元素个数为:" << a1.size() << endl;
//遍历队列容器
auto begin = a1.begin();
auto end = a1.end();
while (begin != end) {
cout << "a1:" << *begin << ",";
begin++;
}
cout << endl;
//访问队列的元素
a1[1] = 1000; //修改元素
cout << a1[1] << endl; //输出元素
a1.at(2) = 1000;//修改元素
cout << a1.at(2) << endl;//访问元素
a1.front() = 1222;//修改首元素
cout << a1.front() << endl; //访问首元素
a1.back() = 3000;//修改尾元素
cout << a1.back() << endl;//访问尾元素
//元素批量操作
a1.emplace(a1.end(),12); //在容器最后加入一个元素12
a1.erase(a1.begin()+2,a1.end()); //移除a1第三个到最后一个之间的所有元素
//插入一个数组到队列中
a1.insert(a1.end(),{12,33,44,55});
for (auto i = a1.begin(); i < a1.end(); ++i) {
cout << "a1:" << *i << ",";
}
cout << endl;
//插入原有队列到队列中
a1.insert(a1.end(),a1.begin(),a1.end());
for (auto i = a1.begin(); i < a1.end(); ++i) {
cout << "a1:" << *i << ",";
}
cout << endl;
//清空所有元素
a1.clear();
std::cout << "Hello, World!" << std::endl;
return 0;
}
代码clone地址:[email protected]:XiaoWang_csdn/cpp_21_deque.git
list<T>(链表容器):是一个长度可变的、由 T 类型元素组成的序列,它以双向链表的形式组织元素,在这个序列的任何地方都可以高效地增加或删除元素(时间复杂度都为常数阶 O(1)),但访问容器中任意元素的速度要比前三种容器慢,这是因为 list<T> 必须从第一个元素或最后一个元素开始访问,需要沿着链表移动,直到到达想要的元素。
代码实例:
#include <iostream>
#include <list> //导入链表容器
#include "assert.h"
using namespace std;
int main() {
//初始化链表容器对象
list<int> MyList; //初始化一个空的list对象
list<int> MyList2(20); //初始化有20个元素的list
list<int> MyList3(4, 0); //初始化有四个元素的list,把四个元素的值都初始化为0
//在链表头部插入元素
MyList.push_front(12);
//在链表尾部插入元素
MyList.push_back(22);
//判定链表是否为空,若不为空则输出链表的元素个数
cout << "链表元素个数:" << (MyList.empty() ? 0 : MyList.size()) << endl;
//使用头部和尾部的引用来改变元素和获取元素
cout << MyList.front() << endl;
MyList.front() = 123;
cout << MyList.front() << endl;
cout << MyList.back() << endl;
MyList.back() = 266;
cout << MyList.back() << endl;
//找到链表的开头与结尾的迭代器来遍历元素: 使用双向迭代器
for (list<int>::iterator i = MyList.begin(); i != MyList.end(); ++i) {
cout << "mylist: " << *i << endl;
}
//替换指定元素的内容
MyList2.assign(3, 12); //把第三个元素的值该为12
//在容器头部插入一个元素
MyList2.emplace_front(22);
//在容器尾部插入一个元素
MyList2.emplace_back(9000);
//删除头部元素
MyList2.pop_front();
//删除尾部元素
MyList2.pop_back();
//在容器指定位置插入元素
MyList2.emplace(MyList2.begin(), 89);
MyList2.insert(MyList2.end(), {34, 55, 79});
cout << "MyList2元素个数: " << MyList2.size() << endl;
//删除容器中的指定元素
MyList2.erase(MyList2.begin());
//将MyList容器添加到MyList2容器的第一个位置
MyList2.splice(MyList2.begin(), MyList);
cout << "MyList2元素个数: " << MyList2.size() << endl;
//删除MyList2容器中等于79的所有元素
MyList2.remove(79);
cout << "MyList2元素个数: " << MyList2.size() << endl;
//删除容器中满足条件的元素
//MyList2.remove_if();
//删除容器中相邻的重复元素,只保留一个
MyList2.unique();
cout<<MyList.size()<<endl;
//合并两个有序的list容器,确保合并后仍然有序,最终会合并到MyList里面
MyList.merge(MyList2);
cout<<MyList.size()<<endl;
for (list<int>::iterator i = MyList.begin(); i !=MyList.end() ; ++i) {
cout<<"mylist: "<< *i<<",";
}
cout<<endl;
//给元素排序
MyList.sort();
for (list<int>::iterator i = MyList.begin(); i !=MyList.end() ; ++i) {
cout<<"mylist: "<< *i<<",";
}
cout<<endl;
//反转容器中的元素
MyList.reverse();
for (list<int>::iterator i = MyList.begin(); i !=MyList.end() ; ++i) {
cout<<"mylist: "<< *i<<",";
}
cout<<endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_22_list.git
forward_list<T>(正向链表容器):和 list 容器非常类似,只不过它以单链表的形式组织元素,它内部的元素只能从第一个元素开始访问,是一类比链表容器快、更节省内存的容器。
实例代码:
#include <iostream>
#include <forward_list> //导入头文件
using namespace std;
int main() {
//初始化单链表容器
forward_list<int > mylsit;
//给链表添加元素
for (int i = 0; i < 10; ++i) {
mylsit.push_front(i);
}
//访问元素
cout<<mylsit.front()<<endl;
mylsit.push_front(0);
//遍历元素
for (auto i = mylsit.begin(); i != mylsit.end(); ++i) {
cout<<*i<<",";
}
cout<<endl;
//给元素排序
mylsit.sort();
//去出重复元素
mylsit.unique();
//清空链表
mylsit.clear();
std::cout << "Hello, World!" << std::endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_23_forward_list.git
- 关联式容器(包括排序容器和哈希容器)
关联式容器存储的元素,都是一个一个的“键值对”( <key,value> ),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。关联式容器可以快速查找、读取或者删除所存储的元素,同时该类型容器插入元素的效率也比序列式容器高。
关联式容器存储的元素,默认会根据各元素的键值的大小做升序排序。关联式容器底层选用了 「红黑树」这种数据结构来组织和存储各个键值对。
关联式容器是非线性的树结构(二叉树结构)。各元素之间没有严格的物理上的顺序关系,元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器共包含四种容器:map、multimap、set 以及 multiset。
关联式容器名称 |
特点 |
map |
定义在 <map> 头文件中,使用该容器存储的数据,其各个元素的键必须是唯一的(即不能重复),该容器会根据各元素键的大小,默认进行升序排序(调用 std::less<T>)。 |
set |
定义在 <set> 头文件中,使用该容器存储的数据,各个元素键和值完全相同,且各个元素的值不能重复(保证了各元素键的唯一性)。该容器会自动根据各个元素的键(其实也就是元素值)的大小进行升序排序(调用 std::less<T>)。 |
multimap |
定义在 <map> 头文件中,和 map 容器唯一的不同在于,multimap 容器中存储元素的键可以重复。 |
multiset |
定义在 <set> 头文件中,和 set 容器唯一的不同在于,multiset 容器中存储元素的值可以重复(一旦值重复,则意味着键也是重复的)。 |
C++ STL 标准库提供了 pair 类模板,用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素<first, second>。故 pair 类模板常常用于创建“键值对”形式的元素。 pair 类模板使用时需引入<utility>头文件。
#include <iostream>
#include <utility> //导入pair模板类头文件
using namespace std;
int main() {
//初始pair
pair<int, string> p1; //调用默认的构造函数(键值对)
p1.first = 1234; //给键赋值
p1.second = "理想"; //给值赋值
//输出键值对
cout << p1.first << " : " << p1.second << endl;
//第二种常用初始化方式
pair<int, string> p2(23, "大v");
//输出键值对
cout << p2.first << " : " << p2.second << endl;
//比较键值对
if (p1 == p2) cout << "p1 = p2" << endl;
else cout << "p1 != p2" << endl;
//交换两个pair
p1.swap(p2);
//输出键值对
cout << p1.first << " : " << p1.second << endl;
cout << p2.first << " : " << p2.second << endl;
std::cout << "Hello, World!" << std::endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_24_pair.git
下面是四种容器的具体实现类:
排序容器包括set 集合容器、multiset多重集合容器、map映射容器、 multimap 多重映射容器;
哈希容器包括 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射、 unordered_multimap 哈希多重映射;
- set 集合容器
代码示范:
#include <iostream>
#include <set> //导入set头文件
using namespace std;
int main() {
/*
* set容器:
* 1,set容器只能存储一种类型的数据,并且会把内部数据按照指定的规则进行排序
* 2,set模板格式: template<class T,less<T>,allocator<T>> class set ;
* 参数含义: class T 数据类型,less<T> 排序规则 ,allocator<T> 分配器对象的类型
*
* */
//创建容器的同时对容器进行初始化,less<int>指定排序规则为升序就是从小到大,greater<int>为降序即从大到小
set<int, less<int>> s{23, 41, 78, 99, 1234, 345};
//遍历容器: 使用双向迭代器遍历
for (auto i = s.begin(); i != s.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//遍历容器: 使用反迭代器遍历(rbegin指向最后一个元素)
for (auto i = s.rend(); i != s.rbegin(); --i) {
cout << *i << ",";
}
cout << endl;
//遍历容器: 使用带const修饰的双向迭代器,不能使用迭代器修改元素
for (auto i = s.cbegin(); i != s.cend(); ++i) {
cout << *i << ",";
}
cout << endl;
//插入元素
s.insert(199009);
s.insert(2314);
s.insert(23);
s.insert(41);
//遍历容器: 使用带const修饰的双向迭代器,不能使用迭代器修改元素
for (auto i = s.cbegin(); i != s.cend(); ++i) {
cout << *i << ",";
}
cout << endl;
//查找元素
set<int>::iterator it = s.find(41);
//输出查找到的元素: 如果存在则迭代器指针指向找到的元素,否则迭代器指向最后一个元素+1的位置
cout << "find elm:" << *it << endl;
//从找到的元素处开始遍历整个容器
while (it != s.end()) {
cout << "find:" << *it << ",";
it++;
}
cout << endl;
//插入元素并接收insert的返回值
pair<set<int>::iterator, bool> ps = s.insert(8888);
//输出插入的元素:若插入成功pair的第二个参数返回true
cout << "first:" << *(ps.first) << "," << "second: " << ps.second << endl;
//遍历容器: 使用双向迭代器遍历
for (auto i = s.begin(); i != s.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//插入一个重复的元素
pair<set<int >::iterator,bool > pairs=s.insert(8888);
if (pairs.second){
cout<<"插入成功,元素为:"<< *(pairs.first)<<endl;
} else cout<<"插入失败,因为元素"<< *(s.find(8888))<<"已经存在"<<endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_27_set.git
- multiset多重集合容器
代码实例:
#include <iostream>
#include <set> //multiset容器使用的头文件也是set
using namespace std;
int main() {
/*
* multiset的特点:
* 1,只存储一种类型的数据,实际上是键和值相等的元素,真正存储的是值;
* 2,放在容器里面的元素默认以升序排列;
* 3,容器中的元素被const修饰,不能直接修改,只能采取先删除在添加的方式;
* 4,加入容器的元素可以重复,这是与set的唯一区别
* */
//创建multiset容器,指定排序规则为降序,并且给定几个初值
multiset<int ,greater<int >> ms{12,34,67,321,0,11,1,2,2,2,2,2};
//返回元素的个数
cout<<ms.size()<<endl;
//返回匹配的元素个数
cout<<ms.count(2)<<endl;
//向容器中添加元素
ms.insert(99);
//删除指定值: 如果容器中有多个值相同,那么会删除所有匹配的值
int num=ms.erase(2); //所有值为2的元素均会被删除,返回被删除的个数
cout<<"已删除"<<num<<"个等于2的元素"<<endl;
//遍历元素
for (auto i = ms.begin(); i != ms.end() ; ++i) {
cout<<*i<<",";
}
cout<<endl;
return 0;
}
完整代码clone仓库:[email protected]:XiaoWang_csdn/cpp_29_multiset.git
- map映射容器
代码实例:
#include <iostream>
#include <map> //导入map所需头文件
#include <utility>
#include <string>
using namespace std;
int main() {
/*
* 使用map经验总结:
* 1,map会根据键值对的大小做升序排列;可以手动指定 map 容器的排序规则,
* 既可以选用 STL 标准库中提供的其它排序规则(比如std::greater<T>),也可以自定义排序规则。
* 2,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改;
* 3, map容器的模板有四个参数:
template < class Key,// 指定键(key)的类型
class T, // 指定值(value)的类型
class Compare = less<Key>,// 指定排序规则,这个参数有默认值
class Alloc = allocator<pair<const Key,T> > // 指定分配器对象的类型,这个参数有默认值
> class map;
4,C++ 11 标准中,还为 map 容器增添了移动构造函数。用于函数返回临时的map对象
* */
//创建map对象: 并指定键和值的类型均为string
map<string, string> testMap;
//使用pair模板对map进行初始化
pair<string, string> pairs("姓名", "李明");
map<string, string> testMap2{pairs};
//使用原有的map初始化新的map
map<string, string> testMap3(testMap2);
//map的操作
for (int i = 0; i < 10; ++i) {
testMap3.emplace(to_string(i), to_string(i + 1)); //向容器中构造新键值对
}
//使用符号[] 新增键值对
testMap3["99"] = "这是99";
//修改map中已存在键的值
testMap3["99"] = "修改为88";
//使用insert插入键值对
// 如果成功插入 pairs,则该迭代器指向新插入的 pairs,bool 值为 true;
//如果插入 pairs 失败,则表明当前 map 容器中存有和 pairs 的键相同的键值对(用 p 表示),此时返回的迭代器指向 p,bool 值为 false。
pair<map<string, string>::iterator, bool> t;
t = testMap.insert(pairs);
//访问插入成功的键值对
cout << "insert-MAP:" << t.first->first << "," << t.first->second << "," << t.second << endl;
//其他两种插入方式
//调用 pair 类模板的构造函数
t = testMap.insert(pair<string, string>{"年龄", "23"});
//访问插入成功的键值对
cout << "insert-map:" << t.first->first << "," << t.first->second << "," << t.second << endl;
//调用 make_pair() 函数
t = testMap.insert(make_pair("薪资", "180万"));
//访问插入成功的键值对
cout << "insert-map:" << t.first->first << "," << t.first->second << "," << t.second << endl;
//插入键值对到指定位置
testMap.insert(testMap.begin(), pairs);
cout << "插入到指定位置:" << testMap.begin()->first << "," << testMap.begin()->second << endl;
//以右值方式插入临时键值对
auto it = testMap.insert(testMap.begin(), pair<string, string>("住址", "北京天安门"));
cout << "以右值方式插入:" << it->first << "," << it->second << endl;
cout << "testMap3.size():" << testMap3.size() << endl;
//插入testMap容器的指定区域到当前容器testMap3
testMap3.insert(testMap.begin(), testMap.end());
cout << "testMap3.size():" << testMap3.size() << endl;
//一次性向容器中插入多个元素
testMap3.insert({
{"种类", "树木"},
{"颜色", "红色"},
{"价值", "10万"}
});
//遍历键值对容器
for (auto i = testMap3.begin(); i != testMap3.end(); ++i) {
cout << i->first << ":" << i->second << endl;
}
//查找键
auto a = testMap3.find("1");//查找成功返回begin()迭代器,查找失败则返回end()迭代器。
//如果查找到,则输出键值对
if (a == testMap3.begin()) {
cout << a->first << " : " << a->second << ";" << endl;
}
//查找不大于指定key的第一个键值
auto b = testMap3.lower_bound("2");
cout << b->first << " : " << b->second << ";" << endl;
//查找大于key的键值对
auto b2 = testMap3.upper_bound("4");
cout << b2->first << " : " << b2->second << ";" << endl;
//返回key的范围的键值对,只有一组值
auto c = testMap3.equal_range("1");
for (auto i = c.first; i != c.second; ++i) {
cout << "equal_range:" << i->first << "," << i->second << endl;
}
//使用重载符[]通过键获取值: 当集合中存在该键时返回该键对对应的值,若不存在则创建该键
cout << "testMap3[1]=" << testMap3["1"] << endl;
//使用at函数获取键值: 推荐使用这种方式获取键值
cout << "4:" << testMap3.at("4") << endl;
//遍历容器,寻找指定元素
for (auto i = testMap3.begin(); i != testMap3.end(); ++i) {
//查找键为5的键值对
if (!i->first.compare("5")) {
cout << i->first << " : " << i->second << endl;
}
}
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_25_map.git
- multimap 多重映射容器
代码示范:
#include <iostream>
#include <map> //导入multimap头文件
using namespace std;
int main() {
//创建multimap对象
multimap<string,string> MyMultiMap;
//向容器中添加元素
MyMultiMap.insert({
{"姓名","李白"},
{"姓名","张三"},
{"姓名","朱元章"},
{"姓名","刘邦"}
});
//访问容器中的元素
for (auto i = MyMultiMap.begin(); i != MyMultiMap.end() ; ++i) {
cout<<i->first<<":"<<i->second<<endl;
}
//less<string>: 以升序排列当前容器中的所有元素
multimap<string,string,less<string>> mp{ {"9姓名","李白"},{"4姓名","张三"},{"1姓名","朱元章"},{"3姓名","刘邦"}};
//访问容器中的元素
for (auto i = mp.begin(); i != mp.end() ; ++i) {
cout<<i->first<<":"<<i->second<<endl;
}
//greater<string>: 以降序排列当前容器中的所有元素
multimap<string,string,greater<string>> mp2 { {"9姓名","李白"},{"4姓名","张三"},{"1姓名","朱元章"},{"3姓名","刘邦"}};
//访问容器中的元素
for (auto i = mp2.begin(); i != mp2.end() ; ++i) {
cout<<i->first<<":"<<i->second<<endl;
}
//查找匹配该键的个数
cout<<"容器的元素为:"<<MyMultiMap.count("姓名")<<endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_26_multimap.git
无序容器 |
功能 |
unordered_map |
存储键值对 <key, value> 类型的元素,其中各个键值对键的值不允许重复,且该容器中存储的键值对是无序的。 |
unordered_multimap |
和 unordered_map 唯一的区别在于,该容器允许存储多个键相同的键值对。 |
unordered_set |
不再以键值对的形式存储数据,而是直接存储数据元素本身(当然也可以理解为,该容器存储的全部都是键 key 和值 value 相等的键值对,正因为它们相等,因此只存储 value 即可)。另外,该容器存储的元素不能重复,且容器内部存储的元素也是无序的。 |
unordered_multiset |
和 unordered_set 唯一的区别在于,该容器允许存储值相同的元素。 |
无序容器的特点:
- 底层实现采用的是哈希表的存储结构;
- 查找根据键查找值非常快,但是遍历元素非常慢;
- 存储的元素是无序,元素所在的位置根据hash算法来确定;
- 实际场景中如果涉及大量遍历容器的操作首选关联式容器;若是通过键获取对应的值,则应首选无序容器。
- unordered_map 哈希映射、
代码实例:
#include <iostream>
#include <unordered_map> //无序map所需头文件
#include <utility>
using namespace std;
int main() {
/*
* unodered_map特点:
* 1,以键值对形式存储数据,键值对不能重复且不允许修改;
* 2,底层采用哈希表存储结构,不会对存储的键值对降序排序;
* 3,总体而言,unodered_map相当于一个无序的map;
* 4,模板原型定义如下:
* template < class Key, //键值对中键的类型
class T, //键值对中值的类型
class Hash = hash<Key>, //容器内部存储键值对所用的哈希函数,仅适用于基本数据类型,不适合自定义的结构体或类。
class Pred = equal_to<Key>, //判断各个键值对键相同的规则
class Alloc = allocator< pair<const Key,T> > // 指定分配器对象的类型
> class unordered_map;
* */
//创建容器
unordered_map<string, string> unmap;
//向容器中添加键值对
unmap.emplace("工作", "结构工程师");
unmap.emplace("职位", "CEO");
//输出元素的个数
cout << unmap.size() << endl;
//使用迭代器输出元素
for (auto i = unmap.begin(); i != unmap.end(); ++i) {
cout << i->first << ":" << i->second << "," << endl;
}
//查找指定元素
unordered_map<string, string>::iterator it = unmap.find("职位");
cout << it->first << " : " << it->second << endl; //输出找到的键值对
//获取元素: 通过键获取值常用的几种方法
cout << unmap["职位"] << endl; //通过 [键名] 的方式访问,如果不存在则创建该键,值为默认值
cout << unmap.at("职位") << endl; //通过at方法获取键的值,若存在该键返回键对应的值,否则抛出out_of_range异常
//使用at查找一个不存在的值,用try catch捕获异常-----推荐用法
try {
cout << unmap.at("职位33") << endl;
} catch (out_of_range) {
cout << "容器中没有该键," << endl;
}
//通过find方法查找元素
unordered_map<string, string>::iterator it2 = unmap.find("职位");
if (it2 == unmap.end()) {
cout << "查找键 --职位-- 失败" << endl;
} else {
cout << it2->first << " : " << it2->second << endl;
}
//遍历整个容器查找元素
for (auto i = unmap.begin(); i != unmap.end(); ++i) {
if (!i->first.compare("职位")) {
cout << "找到键值:" << i->second << endl;
break;
}
}
//插入值: 需要一个pair的模板参数
auto a1 = unmap.insert(pair<string, string>("薪资", "100万")); //传入一个无名的pair模板对象,携带需要插入的键值对
auto a2 = unmap.insert(make_pair("资历", "10年"));
//输出插入结果: 插入成功输出插入的值,插入失败输出空
cout << a1.first->first << " : " << a1.first->second << endl;
cout << a2.first->first << " : " << a2.first->second << endl;
//删除容器的元素
int deleteEleCount = unmap.erase("资历"); //返回一个整数表示成功删除的键值对的数量
cout << "共删除元素个数为:" << deleteEleCount << endl;
cout << "元素个数为:" << unmap.size() << endl;
//这样会删除所有元素
auto deleteCounts = unmap.erase(unmap.begin(), unmap.end()); //返回一个指向被删除的最后一个键值对之后一个位置的迭代器。
for (auto i = unmap.begin(); i != unmap.end(); ++i) {
cout << i->first << "," << i->second << endl;
}
cout << "元素个数为:" << unmap.size() << endl;
//清空容器
unmap.clear();
cout << "元素个数为:" << unmap.size() << endl;
return 0;
}
完整代码clone地址:[email protected]:XiaoWang_csdn/cpp_29_unoderedmap.git
- unordered_multimap 哈希多重映射;
代码实例:
#include <iostream>
#include <unordered_map>
#include "string"
using namespace std;
int main() {
/*
* unodered_multimap特点:
* 1,底层采用哈希表存储元素;
* 2,元素存储无序;
* 3,可以存储重复的键值对;
* 4,其他特性与unodered_map完全一致;
* */
//创建容器对象
unordered_multimap<string,string> ump;
//插入元素
ump.emplace("c++","底层开发");
ump.emplace("c++","操作系统开发");
ump.emplace("c++","游戏开发");
ump.emplace("c#","桌面软件开发");
cout<<"容器元素个数为:"<<ump.size()<<endl;
//遍历元素
for (auto i = ump.begin(); i != ump.end() ; ++i) {
cout<<i->first<<":"<<i->second<<",";
}
cout<<endl;
std::cout << "Hello, World!" << std::endl;
return 0;
}
完整代码clone地址: [email protected]:XiaoWang_csdn/cpp_30_unoderedmultimap.git
- unordered_set 哈希集合、unordered_multiset 哈希多重集合、
实例代码:
#include <iostream>
#include <unordered_set> //无序set头文件
#include <string>
using namespace std;
int main() {
/*
* unodered_set特点:
* 1,容器内直接存储值,不存储键;;
* 2,容器内的元素不能重复,不能被修改;
* 3,内部的元素均是无序存储的;
* */
//创建容器: 该容器元素不能重复
unordered_set<string> uset;
//向容器中插入元素
uset.emplace("李白");
uset.emplace("刘邦");
//输出元素个数
cout << uset.size() << endl;
//遍历元素
for (auto i = uset.begin(); i != uset.end(); ++i) {
cout << *i << ",";
}
cout << endl;
/*
* unodered_multiset特点:
* 1,容器内直接存储值,不存储键;;
* 2,容器内的元素可以重复,不能被修改;
* 3,内部的元素均是无序存储的;
* */
//创建容器: 该容器可以存储重复的元素
unordered_multiset<string> ums;
//添加元素
ums.emplace("汽车");
ums.emplace("汽车");
ums.emplace("汽车");
ums.emplace("火车");
ums.emplace("轮船");
ums.emplace("飞机");
//输出元素个数
cout<<ums.size()<<endl;
//获取指定值
auto s=ums.find("汽车");
//把所有匹配的值全部输出
for (; s != ums.end() ; ++s) {
cout<<*s<<endl;
}
//遍历集合
for (auto i = ums.begin(); i != ums.end() ; ++i) {
cout<<*i<<",";
}
cout<<endl;
//删除容器数据
int w=ums.erase("汽车");
cout<<"被删除的元素个数为:"<<w<<endl;
//清空容器
ums.clear();
return 0;
}
完整代码clone地址: [email protected]:XiaoWang_csdn/cpp_31_unoderedset.git
- 适配器(配接器)、
容器适配器 |
基础容器筛选条件 |
默认使用的基础容器 |
stack |
基础容器需包含以下成员函数: empty() size() back() push_back() pop_back() 满足条件的基础容器有 vector、deque、list。 |
deque |
queue |
基础容器需包含以下成员函数: empty() size() front() back() push_back() pop_front() 满足条件的基础容器有 deque、list。 |
deque |
priority_queue |
基础容器需包含以下成员函数: empty() size() front() push_back() pop_back() 满足条件的基础容器有vector、deque。 |
vector |
适配器分为容器适配器和迭代器适配器,容器适配器包括Stack、Queue、priority_queue; 迭代器适配器包括输入迭代器、输出迭代器、前向迭代器、双向迭代器以及随机访问迭代器。在这些基础的迭代器基础上又有五种实现,分别是反向迭代器适配器、插入型迭代器适配器、流迭代器适配器、流缓冲区迭代器适配器、移动迭代器适配器。
Stack |
是一个封装了 deque<T> 容器的适配器类模板,默认实现的是一个后入先出(Last-In-First-Out,LIFO)的压入栈; |
Queue |
是一个封装了 deque<T> 容器的适配器类模板,默认实现的是一个先入先出(First-In-First-Out,LIFO)的队列; |
priority_queue |
是一个封装了 vector<T> 容器的适配器类模板,默认实现的是一个会对元素排序,从而保证最大元素总在队列最前面的队列; |
反向迭代器(reverse_iterator) |
又称“逆向迭代器”,其内部重新定义了递增运算符(++)和递减运算符(--),专门用来实现对容器的逆序遍历。 |
安插型迭代器(inserter或者insert_iterator) |
通常用于在容器的任何位置添加新的元素,需要注意的是,此类迭代器不能被运用到元素个数固定的容器(比如 array)上。 |
流迭代器(istream_iterator / ostream_iterator)
|
输入流迭代器用于从文件或者键盘读取数据;相反,输出流迭代器用于将数据输出到文件或者屏幕上。 |
移动迭代器(move_iterator) |
此类型迭代器是 C++ 11 标准中新添加的,可以将某个范围的类对象移动到目标范围,而不需要通过拷贝去移动。 |
Stack实例代码
#include <iostream>
#include <stack> //导入stack栈头文件
#include <list>
using namespace std;
int main() {
/*
* stack的特点:
* 1,其原理就是栈的实现;
* 2,所有加入的元素均是后进先出;
* 3,如果使用其他容器来初始化,必须在stack的第二参数指明容器的类型;
* */
//创建stack
stack<int> stack1; //创建一个空栈
list<int> value{12, 33, 44, 55}; //初始化一个list给stack用
stack<int, list<int>> stack2(value); //使用list初始化stack
//常用成员函数使用实例
cout<<(stack2.empty() ? "栈为空" : "栈非空")<< endl; //判定容器是否为空
cout<<"stack size= "<<stack2.size()<<endl;
cout<<"栈顶元素为:"<<(stack2.empty()? -1 : stack2.top())<<endl; //返回栈顶元素,若无元素则返回-1
//在栈顶放入一个元素
stack2.push(999);
//弹出的栈顶元素
stack2.pop();
//向栈中一次加入多个元素
//stack2.emplace(22,33,99);
//遍历栈元素: 依次弹出元素
for (auto i = 0; i < stack2.size()-1 ; ++i) {
stack2.pop();
i++;
cout<<stack2.top()<<",";
}
cout<<endl;
//交换两个栈
//stack2.swap(stack1);
std::cout << "Hello, World!" << std::endl;
return 0;
}
Queue实例代码
#include <iostream>
#include <queue> //包含队列头文件
using namespace std;
int main() {
/*
* queue的特点:
* 1,所有元素均会按照顺序进入队列;
* 2,所有元素均会按照先进先出的原则进行出队列;
* 3,底层默认采用deque;
* */
//创建queue
queue<int> qu;
//添加元素
for (int i = 0; i <10; ++i) {
qu.push(i*100-32); //在队列尾部添加元素
}
//常用函数
cout<<"队列是否为空:"<<qu.empty()<<endl;
cout<<"队列中的元素个数:"<<qu.size()<<endl;
cout<<"队列的第一个元素:"<<qu.front()<<endl;
cout<<"队列的最后一个元素:"<<qu.back()<<endl;
qu.emplace(99); //直接在队尾添加一个元素
cout<<"新添加的元素为:"<<qu.back()<<endl;
qu.pop();//删除队头元素
//qu.swap(newqueue); //交换两个队列元素
cout<<"队列是否为空:"<<qu.empty()<<endl;
cout<<"队列中的元素个数:"<<qu.size()<<endl;
cout<<"队列的第一个元素:"<<qu.front()<<endl;
cout<<"队列的最后一个元素:"<<qu.back()<<endl;
return 0;
}
priority_queue实例代码
#include <iostream>
#include <queue>
using namespace std;
int main() {
/*
* priority_queue特点:
* 1,元素从前端出,从后端进;
* 2,最大的元素最先出;优先级最大的元素最先出队列;
* 3,priority_queue也被称为优先级队列;
* */
//创建priority_queue适配器
priority_queue<int> values;
//添加元素
values.push(123);
values.push(990);
values.push(120);
cout << "队列是否为空:" << (values.empty() ? "是" : "否") << endl;
cout << "队列大小:" << values.size() << endl;
cout << "第一个元素是:" << values.top() << endl;
//添加元素
values.emplace(998);
//移除元素的同时,访问头元素
while (!values.empty()) {
cout << values.top() << ",";
values.pop();
}
cout << endl;
return 0;
}
- 迭代器、
反向迭代器(reverse_iterator)实例代码
reverse_iterator重载的运算符 |
|
重载运算符 |
功能 |
operator* |
以引用的形式返回当前迭代器指向的元素。 |
operator+ |
返回一个反向迭代器,其指向距离当前指向的元素之后 n 个位置的元素。此操作要求基础迭代器为随机访问迭代器。 |
operator++ |
重载前置 ++ 和后置 ++ 运算符。 |
operator+= |
当前反向迭代器前进 n 个位置,此操作要求基础迭代器为随机访问迭代器。 |
operator- |
返回一个反向迭代器,其指向距离当前指向的元素之前 n 个位置的元素。此操作要求基础迭代器为随机访问迭代器。 |
operator-- |
重载前置 -- 和后置 -- 运算符。 |
operator-= |
当前反向迭代器后退 n 个位置,此操作要求基础迭代器为随机访问迭代器。 |
operator-> |
返回一个指针,其指向当前迭代器指向的元素。 |
operator[n] |
访问和当前反向迭代器相距 n 个位置处的元素。 |
#include <iostream>
#include <iterator> //迭代器头文件
#include <vector>
using namespace std;
int main() {
//创建一个vector容器
vector<int> vt{12, 33, 44, 66, 77};
//创建并初始化反向迭代器: 迭代器类型指定为vector<int>::iterator,然后把vt的最后一个元素的指针作为初始值
reverse_iterator<vector<int>::iterator> reverseIterator(vt.end());
//使用被reverse_iterator重载的运算符访问元素
cout << *reverseIterator << endl; //输出77,一开始反向迭代器指向的是vector的最后一个元素
cout << *(reverseIterator + 2) << endl;//输出44
cout << reverseIterator[3] << endl;//输出33
//base方法返回基础迭代器对象
auto baseIt = reverseIterator.base(); //相当于vector.end();
cout << *(baseIt-1) << endl; //因为vector.end();是没指向最后一个元素的后一个末尾位置的,在访问容器元素时需要-1
return 0;
}
安插型迭代器(inserter或者insert_iterator)实例代码
迭代器适配器 |
功能 |
back_insert_iterator |
在指定容器的尾部插入新元素,但前提必须是提供有 push_back() 成员方法的容器(包括 vector、deque 和 list)。 |
front_insert_iterator |
在指定容器的头部插入新元素,但前提必须是提供有 push_front() 成员方法的容器(包括 list、deque 和 forward_list)。 |
insert_iterator |
在容器的指定位置之前插入新元素,前提是该容器必须提供有 insert() 成员方法。 |
#include <iostream>
#include <iterator>
#include <vector>
#include <list>
using namespace std;
int main() {
vector<int> vt;
//定义尾部插入迭代器
back_insert_iterator<vector<int>> insertIterator(vt); //需要明确类型和需要插入的对象
//插入一些列元素到vt尾部
for (int i = 0; i < 10; ++i) {
insertIterator = i; //这一句相当于vector.push_back(i);
}
//输出vt
for (auto i = vt.begin(); i != vt.end(); ++i) {
cout << *i << ",";
}
cout << endl;
list<int> it;
//定义头部插入迭代器
front_insert_iterator<list<int>> frontInsertIterator(it);
//向头部插入一些列元素
for (int i = 0; i < 30; ++i) {
frontInsertIterator = ((i % 2 == 0) ? i : i + i);
}
//遍历元素
for (auto i = it.begin(); i != it.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//定义基础迭代器参数解释: it是要插入的集合,++it.begin()是指定要插入的位置,这里指头部。如果需要在尾部插入则换为it.end();
insert_iterator<list<int >> insert_it(it,it.begin());
//向头部插入一些列元素
for (int i = 0; i < 30; ++i) {
insert_it =i;
}
//遍历元素
for (auto i = it.begin(); i != it.end(); ++i) {
cout << *i << ",";
}
cout << endl;
return 0;
}
流迭代器(istream_iterator / ostream_iterator)、流缓冲区迭代器(istreambuf_iterator / ostreambuf_iterator)实例代码:
流迭代器也是一种迭代器适配器,不过和之前讲的迭代器适配器有所差别,它的操作对象不再是某个容器,而是流对象。
将绑定到输入流对象的迭代器称为输入流迭代器(istream_iterator),其可以用来读取输入流中的数据;
将绑定到输出流对象的迭代器称为输出流迭代器(ostream_iterator),其用来将数据写入到输出流中。
#include <iostream>
#include <iterator>
#include <vector>
using namespace std;
int main() {
//创建输入流迭代器对象
istream_iterator<int> ins(cin); //把输入流对象cin作为初始化参数给istream_iterator
int value = *ins; //使用变量保存输入的值
cout << value << endl;//输出变量
//创建输出流迭代器对象
ostream_iterator<int> out(cout, ","); //里面的逗号代表需要设置的分隔符
//向输出流依次写入多个数据
for (int i = 0; i < 5; ++i) {
*out = i; //相当于cout<<i ;
}
cout << endl;
//输出流迭代器配合copy函数使用
vector<int> vt{23, 99, 100, 34545, 789};
//将容器的元素写入到输出流中
copy(vt.begin(), vt.end(), out); //控制台会输出: 23,99,100,34545,789,
//输入流缓冲区迭代器使用示例
//创建结束流缓冲区迭代器
istreambuf_iterator<char> eos;
//创建一个从输入缓冲区读取字符元素的迭代器
istreambuf_iterator<char> iit(cin);
string mystring;
cout << "向缓冲区输入元素:\n";
//不断从缓冲区读取数据,直到读取到 EOF 流结束符
while (iit != eos) {
mystring += *iit++;
}
//创建一个和输出流缓冲区相关联的迭代器
ostreambuf_iterator<char> out_it(cout); // stdout iterator
//向输出流缓冲区中写入字符元素
*out_it = 'S';
*out_it = 'T';
*out_it = 'L';
//和 copy() 函数连用
string mstring("\nCSDN");
//将 mystring 中的字符串全部写入到输出流缓冲区中
copy(mystring.begin(), mystring.end(), out_it);
return 0;
}
移动迭代器(move_iterator)实例代码
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
using namespace std;
int main() {
//定义一个基础容器
typedef vector<string>::iterator MyIterator;
//创建一个容器
vector<string> vt{"hello", "world", "are you ok?", "我是vector"};
//创建移动迭代器
move_iterator<MyIterator> begin = make_move_iterator(vt.begin());
move_iterator<MyIterator> end = make_move_iterator(vt.end());
//使用一定迭代器
vector<string> vt2(begin.base(), end.base()); //base方法返回基础迭代器
//遍历容器
for (auto v: vt) {
cout << "vt: " << v << ",";
}
cout << endl;
//遍历容器
for (auto v: vt2) {
cout << "vt: " << v << ",";
}
cout << endl;
return 0;
}
常用辅助函数:
迭代器辅助函数 |
功能 |
advance(it, n) |
it 表示某个迭代器,n 为整数。该函数的功能是将 it 迭代器前进或后退 n 个位置。 |
distance(first, last) |
first 和 last 都是迭代器,该函数的功能是计算 first 和 last 之间的距离。 |
begin(cont) |
cont 表示某个容器,该函数可以返回一个指向 cont 容器中第一个元素的迭代器。 |
end(cont) |
cont 表示某个容器,该函数可以返回一个指向 cont 容器中最后一个元素之后位置的迭代器。 |
prev(it) |
it 为指定的迭代器,该函数默认可以返回一个指向上一个位置处的迭代器。注意,it 至少为双向迭代器。 |
next(it) |
it 为指定的迭代器,该函数默认可以返回一个指向下一个位置处的迭代器。注意,it 最少为前向迭代器。 |
代码示例:
#include <iostream>
#include <forward_list>
#include <iterator>
#include <vector>
using namespace std;
int main() {
forward_list<int> MyList{11, 22, 33, 44, 5566, 12, 311, 231, 221, 33443, 677, 8896};
forward_list<int>::iterator it = MyList.begin();
//迭代器常用函数
advance(it, 4); //使用advance迭代器把位置向前移动4个位置
cout << *it << endl;
vector<int> vt{12,45,67,89,90};
vector<int>::iterator it2=vt.end();
advance(it2, -3); //使用advance迭代器把位置向后移动2个位置
cout << *it2 << endl;
//distance用于求指定区间的元素个数
int count=distance(MyList.begin(),MyList.end());
cout<<"distance: "<<count<< endl;
//使用全局的begin和end遍历容器
for (auto i = begin(MyList); i != end(MyList) ; ++i) {
cout<<*i<<",";
}
cout<<endl;
return 0;
}
- 算法、
排序算法:对数据进行排序,其排序规则由编程者之指定。
#include <iostream>
#include <algorithm> //导入算法头文件
#include <vector>
#include <list>
using namespace std;
//以函数对象的方式自定义排序规则
class MyComp {
public:
bool operator()(int i, int j) {
return (i > j);
}
};
int main() {
//1,排序函数 sort
vector<int> vt{10, 1, 99, 12, 7, 3, 55, 32, 20, 12, 56};
//使用sort对容器内的元素进行排序,并指定排序规则: 降序
sort(vt.begin(), vt.end(), less<int>());
//遍历容器
for (auto i = vt.begin(); i != vt.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//2,stable_sort() :在不改变相等元素的相对位置的前提下,对元素降序排序
vector<int> vt2{10, 1, 99, 12, 7, 3, 55, 32, 20, 12, 56};
//使用stable_sort()对容器内的元素在不改变相等元素的相对位置的前提下进行排序: 这里指定的是降序
stable_sort(vt2.begin(), vt2.end(), less<int>());
//遍历元素查看遍历结果
for (auto i = vt2.begin(); i != vt2.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//3,partial_sort() :对指定区间的n个元素进行排序,最后把已排序的元素全部依次放至头部,
// partial_sort只适用于array、vector、deque
vector<int> vt3{10, 1, 99, 12, 7, 3, 55, 32, 20, 12, 56};
partial_sort(vt3.begin(), vt3.begin() + 3, vt3.end(), less<int>()); //以降序排列vt3.begin() + 3个在整个容器中的元素到头部
for (auto i = vt3.begin(); i != vt3.end(); ++i) {
cout << *i << ","; //输出结构:1,3,7,99,12,10,55,32,20,12,56,
}
cout << endl;
//4,partial_sort_copy(): 对容器指定区间内的元素进行n个元素排序,排序完成后不改变原容器,而是复制到一个新的容器
vector<int> vt4{10, 1, 99, 12, 7, 3, 55, 32, 20, 12, 56};
vector<int> vts(vt4.size()); //用于容纳排序后的元素
partial_sort_copy(vt4.begin() + 2, vt4.end(), vts.begin(), vts.end());
//遍历元素
for (auto i = vts.begin(); i != vts.end(); ++i) {
cout << *i << ","; //输出结果: 3,7,12,12,20,32,55,56,99,0,0,
}
cout << endl;
/*
* nth_element() 函数的功能,当采用默认的升序排序规则(std::less<T>)时,
* 该函数可以从某个序列中找到第 n 小的元素 K,并将 K 移动到序列中第 n 的位置处。
* 不仅如此,整个序列经过 nth_element() 函数处理后,所有位于 K 之前的元素都比 K 小,
* 所有位于 K 之后的元素都比 K 大。
*
* nth_element() 函数的排序规则可以自定义为降序排序,此时该函数会找到第 n 大的
元素 K 并将其移动到第 n 的位置处,同时所有位于 K 之前的元素都比 K 大,
所有位于 K 之后的元素都比 K 小。
nth_element() 函数有以下 2 种语法格式:
//排序规则采用默认的升序排序
void nth_element (RandomAccessIterator first,
RandomAccessIterator nth,
RandomAccessIterator last);
//排序规则为自定义的 comp 排序规则
void nth_element (RandomAccessIterator first,
RandomAccessIterator nth,
RandomAccessIterator last,
Compare comp);
其中,各个参数的含义如下:
first 和 last:都是随机访问迭代器,[first, last) 用于指定该函数的作用范围(即要处理哪些数据);
nth:也是随机访问迭代器,其功能是令函数查找“第 nth 大”的元素,并将其移动到 nth 指向的位置;
comp:用于自定义排序规则。
*/
vector<int> vt6{12, 1, 2, 3, 0, 9};
//查找比vt6.begin()+2大的元素,并把位置移动到该元素指向的位置
nth_element(vt6.begin(), vt6.begin() + 2, vt6.end());
cout << "第一次排序结果:" << endl;
for (auto i = vt6.begin(); i != vt6.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//指定自定义的排序规则
nth_element(vt6.begin(), vt6.begin(), vt6.end(), less<int>());
cout << "第二次排序结果:" << endl;
for (auto i = vt6.begin(); i != vt6.end(); ++i) {
cout << *i << ",";
}
cout << endl;
/*is_sorted用于判断当前的数据是否是需要的排序序列
is_sorted() 函数有 2 种语法格式,分别是:
判断 [first, last) 区域内的数据是否符合 std::less<T> 排序规则,即是否为升序序列
bool is_sorted (ForwardIterator first, ForwardIterator last);
判断 [first, last) 区域内的数据是否符合 comp 排序规则
bool is_sorted (ForwardIterator first, ForwardIterator last, Compare comp);*/
vector<int> ve{8, 1, 2, 3, 9}; //无序序列
list<int> mylist{2, 6, 9, 12};
//判断当前容器内的元素是否有序
if (!is_sorted(ve.begin(), ve.end(), MyComp())) {
cout << "对ve容器内的元素开始排序" << endl;
sort(ve.begin(), ve.end(), MyComp()); //降序
//输出元素
for (auto i = ve.begin(); i != ve.end(); ++i) {
cout << *i << ",";
}
cout << endl;
}
//对有序的序列进行判断
if (is_sorted(mylist.begin(), mylist.end())) {
cout << "当前集合有序,无序重复排序" << endl;
}
/*is_sorted_until() 函数能检测出某个序列是否有序,
并返回一个正向迭代器,该迭代器指向的是当前序列中第一个破坏有序状态的元素。*/
vector<int> ve2{8, 1, 2, 3, 9}; //无序序列
list<int> mylist2{2, 6, 9, 12};
vector<int>::iterator it = is_sorted_until(ve2.begin(), ve2.end(), MyComp());
cout << "第一个破坏有序序列的元素是:" << *it << endl;
//判断第一个破坏有序序列的元素是否是ve2.end(),如果不是则对元素进行重新排序
if (it!=ve2.end())
//开始对元素进行排序
sort(ve2.begin(), ve2.end(), MyComp());
//遍历元素
for (auto i = ve2.begin(); i != ve2.end(); ++i) {
cout << "ve2: " << *i << ",";
}
cout << endl;
//判断第一个破坏有序序列的元素是否是mylist2.end(),如果不是则对元素进行重新排序
list<int>::iterator it2 = is_sorted_until(mylist2.begin(), mylist2.end());
if (it2==mylist2.end())
cout<<"元素已经有序,不需要排序"<<endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_40_algorithm.git
合并算法: 将两个有序序列合并为一个有序序列
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
int main() {
/* merge() 函数用于将 2 个有序序列合并为 1 个有序序列,
前提是这 2 个有序序列的排序规则相同(要么都是升序,要么都是降序)。
并且最终借助该函数获得的新有序序列,其排序规则也和这 2 个有序序列相同。*/
//申明两个有序的序列
int a[] = {1, 7, 8, 9, 12};
int b[] = {3, 5, 7, 22, 99};
//申明新的容器,用于存储新生成的有序序列
vector<int> all(10);
//将两个序列合并为一个有序序列: merge() 函数底层是通过拷贝的方式实现合并操作的,对原集合的顺序不影响。
merge(a, a + 5, b, b + 5, all.begin());
//输出合并后的有序序列
for (auto i = all.begin(); i != all.end(); ++i) {
cout << *i << ",";
}
cout << endl;
/* 第二个合并排序函数inplace_merge
inplace_merge() 函数则将最终合并的有序序列存储在 [first, last) 区域中。*/
//一个存储了两个有序序列的数组
int arrays[] = {1, 4, 7, 9, 12, 2, 5, 9, 11, 234, 678, 999};
//合并有序序列
inplace_merge(arrays, arrays + 5, arrays + 12);
//遍历有序序列
for (int i = 0; i < 12; ++i) {
cout << arrays[i] << ",";
}
cout << endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_41_merge.git
查找算法: 在指定范围内查找和目标元素值相等的第一个元素
#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
using namespace std;
//定义查找规则: 判断是不是偶数
class mycom {
public:
bool operator()(const int &i) {
return (i % 2 == 0);
}
};
class mycomp2 {
public:
bool operator()(const int &i, const int &j) {
return (i == j );
}
};
int main() {
/* find() 函数本质上是一个模板函数,用于在指定范围内查找和目标元素值相等的第一个元素。
如下为 find() 函数的语法格式:
InputIterator find (InputIterator first, InputIterator last, const T& val);
其中,first 和 last 为输入迭代器,[first, last) 用于指定该函数的查找范围;val 为要查找的目标元素。
该函数会返回一个输入迭代器,当 find() 函数查找成功时,其指向的是在 [first, last) 区域
内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last 相同。
find() 函数的底层实现,其实就是用==运算符将 val 和 [first, last) 区域内
的元素逐个进行比对。故[first, last) 区域内的元素必须支持==运算符。*/
//申明一个容器和一个数组
vector<int> MyVect{23, 55,55, 13, 87, 32, 89, 21};
string array[] = {"hello", "world", "hi"};
//分别使用find函数在容器和数组中查找元素: 查找成功返回元素,查找失败返回end();
vector<int>::iterator it = find(MyVect.begin(), MyVect.end(), 89);
string *x = find(array, array + 3, "hello");
if (it != MyVect.end())
cout << *it << endl;
if (x != NULL)
cout << *x << endl;
//find_if()函数 : 使用自定义的查找规则查找函数(谓词函数)
vector<int> vt{1, 2, 3, 12, 12,24, 33, 5, 1, 80};
//查找第一个为偶数的数
vector<int>::iterator it2 = find_if(vt.begin(), vt.end(), mycom());
if (it2 != vt.end())
cout << *it2 << endl;
//find_if_not() 函数则用于查找第一个不符合函数规则的元素
//查找第一个为不为偶数的数
vector<int>::iterator it3 = find_if_not(vt.begin(), vt.end(), mycom());
if (it3 != vt.end())
cout << *it3 << endl;
//find_end()用于查找序列最后一次出现的位置
/* find_end() 函数的语法格式有 2 种:
//查找序列 [first1, last1) 中最后一个子序列 [first2, last2)
ForwardIterator find_end (ForwardIterator first1, ForwardIterator last1,
ForwardIterator first2, ForwardIterator last2);
//查找序列 [first2, last2) 中,和 [first2, last2) 序列满足 pred 规则的最后一个子序列
ForwardIterator find_end (ForwardIterator first1, ForwardIterator last1,
ForwardIterator first2, ForwardIterator last2,
BinaryPredicate pred);*/
//查找元素序列1,2,3最后一次出现的位置
int T[] = {1, 2, 3};
vector<int>::iterator it4 = find_end(vt.begin(), vt.end(), T, T + 3);
cout << "最后一次出现的位置为:" << it4 - vt.begin() << ",元素值为:" << *it4 << endl;
//查找元素序列能整除 3,4 的序列的最后一次出现的位置
int T2[] = {3, 4};
vector<int>::iterator it5 = find_end(vt.begin(), vt.end(), T2, T2 + 2, mycomp2());
cout << "能整除 3,4序列的元素最后一次出现的位置为:" << it5 - vt.begin() << ",元素值为:" << *it5 << endl;
//find_first_of(): 在 A 序列中查找和 B 序列中任意元素相匹配的第一个元素
int T3[] = {5, 1};
//在vt容器中查找序列T3, 返回匹配的第一个元素
vector<int>::iterator it6 = find_first_of(vt.begin(), vt.end(), T3, T3 + 2);
if (it6 != vt.end())
cout << "元素值为:" << *it6 << endl;
//在vt容器中查找序列T3, 遵循自定义规则返回匹配的第一个元素
vector<int>::iterator it7 = find_first_of(vt.begin(), vt.end(), T3, T3 + 2, mycomp2());
if (it7 != vt.end())
cout << "元素值为:" << *it7 << endl;
//adjacent_find() 函数用于在指定范围内查找 2 个连续相等的元素
vector<int>::iterator it8 = adjacent_find(vt.begin(), vt.end());
if (it8 != vt.end())
cout << "it8的元素值为:" << *it8 << endl;
//adjacent_find()加入自定义匹配规则
vector<int>::iterator it9 = adjacent_find(vt.begin(), vt.end(),mycomp2());
if (it9 != vt.end())
cout << "it9的元素值为:" << *it9<< endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_42_find.git
搜索算法:查找匹配的序列第一次出现的位置
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//定义排序规则
class comp {
public:
bool operator()(const int &i, const int &j) {
return (i % j == 0);
}
};
int main() {
/*
* search() 函数用于在序列 A 中查找序列 B 第一次出现的位置。
*
* search() 函数也提供有以下 2 种语法格式:
//查找 [first1, last1) 范围内第一个 [first2, last2) 子序列
ForwardIterator search (ForwardIterator first1, ForwardIterator last1,
ForwardIterator first2, ForwardIterator last2);
//查找 [first1, last1) 范围内,和 [first2, last2) 序列满足 pred 规则的第一个子序列
ForwardIterator search (ForwardIterator first1, ForwardIterator last1,
ForwardIterator first2, ForwardIterator last2,
BinaryPredicate pred);
* */
//原始数据集
vector<int> vt{1, 2, 3, 12, 24, 36, 34, 56, 1, 2, 3, 0, 98, 12, 12, 12};
//想要查找的数据集
int arr[] = {1, 2, 3};
//查找集合: 1,2,3
vector<int>::iterator it = search(vt.begin(), vt.end(), arr, arr + 3);
//获取查找的集合集起始位置
if (it != vt.end()) {
cout << "集合1,2,3查找的起始位置为:" << it - vt.begin() << endl;
}
//使用自定义规则查找集合: 1,2,3
vector<int>::iterator it2 = search(vt.begin(), vt.end(), arr, arr + 3, comp());
if (it2 != vt.end()) {
cout << "查找到的元素的起始位置为:" << it2 - vt.begin() << endl;
}
/*//search_n()在指定区域内查找第一个符合要求的子序列。
search_n() 函数的语法格式如下:
//在 [first, last] 中查找 count 个 val 第一次连续出现的位置
ForwardIterator search_n (ForwardIterator first, ForwardIterator last,
Size count, const T& val);
//在 [first, last] 中查找第一个序列,该序列和 count 个 val 满足 pred 匹配规则
ForwardIterator search_n ( ForwardIterator first, ForwardIterator last,
Size count, const T& val, BinaryPredicate pred );*/
//查找容器中第一个{12,12,12}
vector<int>::iterator sn = search_n(vt.begin(), vt.end(), 3, 12);
if (sn != vt.end()) {
cout << "位置为:" << sn - vt.begin() << ",值为:" << *sn << endl;
}
return 0;
}
[email protected]:XiaoWang_csdn/cpp_43_search.git
分组算法:把元素按照指定的规则分为两组,一组为符合规则的序列集合,另一组为不符合规则的序列集合。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//自定义筛选规则: 筛选集合中的偶数
class comp {
public:
bool operator()(const int &i) {
return (i % 2 == 0);
}
};
int main() {
/* partition 可直译为“分组”,partition() 函数可根据用户自定义的筛选规则,
重新排列指定区域内存储的数据,使其分为 2 组,第一组为符合筛选条件的数据,
另一组为不符合筛选条件的数据。
如下为 partition() 函数的语法格式:
ForwardIterator partition (ForwardIterator first,
ForwardIterator last,
UnaryPredicate pred);
其中,first 和 last 都为正向迭代器,其组合 [first, last) 用于指定该函数的作用范围;
pred 用于指定筛选规则。
*/
vector<int> vt = {1, 2, 5, 7, 8, 11, 14, 15, 102};
//按照指定规则对元素的集合进行分组: partition会改变元素位置
vector<int>::iterator it = partition(vt.begin(), vt.end(), comp());
//遍历分组后的集合
for (auto i = vt.begin(); i != vt.end(); ++i) {
cout << *i << ",";
}
cout << endl;
cout << *it << endl;
vector<int> vt2 = {1, 2, 5, 7, 8, 11, 14, 15, 102};
//stable_partition():在分组的同时保证不改变各组中元素的相对位置
vector<int>::iterator it2 = stable_partition(vt.begin(), vt.end(), comp());
//遍历分组后的集合
for (auto i = vt2.begin(); i != vt2.end(); ++i) {
cout << *i << ",";
}
cout << endl;
cout << *it2 << endl;
//partition_copy() 函数不会对原序列做任何修改,以复制的方式将序列中各个元组“分组”到其它的指定位置存储。
vector<int> vt3 = {1, 2, 5, 7, 8, 11, 14, 15, 102}; //原始数据集合
int first[10] = {0}; //存储第一组帅选的结果集
int second[10] = {0}; //存储第二组帅选的结果集
//开始筛选分组
pair<int *, int *> result = partition_copy(vt3.begin(), vt3.end(), first, second, comp());
cout << "first:";
//遍历第一组集合
for (int *i = first; i < result.first; ++i) {
cout << *i << ",";
}
cout << endl;
cout << "second:";
//遍历第二组集合
for (int *i = second; i < result.second; ++i) {
cout << *i << ",";
}
cout << endl;
//partition_point(): 在已分好组的数据中找到分界位置
vector<int> vt4 = {10, 12, 14, 16, 0, 1, 3, 5, 7}; //原始数据集合
//根据筛选规则找到数据的分界
vector<int>::iterator iter = partition_point(vt4.begin(), vt4.end(), comp());
cout << "第一组数据:";
for (auto i = vt4.begin(); i != iter; ++i) {
cout << *i << ",";
}
cout << endl;
cout << "第二组数据:";
for (auto i = iter; i != vt4.end(); ++i) {
cout << *i << ",";
}
cout << endl;
cout << "分界位置为:" << *iter << endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_44_partition.git
查找算法: 在指定范围的集合中,查找需要的元素,一把是返回bool值,存在元素返回true,不存在元素返回false
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//自定义排序规则
class comp {
public:
bool operator()(const int &i, const int &j) {
return i > j;
}
};
int main() {
/*
* lower_bound()、upper_bound()、equal_range() 以及 binary_search()
这 4 个查找函数,它们的底层实现采用的都是二分查找的方式。
注意: 用于二分查找的元素集必须是有序的,否则查找结果无意义。
*/
int a[] = {2, 5, 5, 6, 5, 7, 5, 10};
vector<int> vt{12, 12, 11, 50, 12, 9, 12, 6};
//lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素
//查找第一个不小于7的值
int *pInt = lower_bound(a, a + 5, 7);
cout << "第一个不小于7的值是:" << *pInt << endl;
//查找符合规则的元素集
auto it = lower_bound(vt.begin(), vt.end(), 7, comp());
cout << "第一个符合规则的值是:" << *it << endl;
//upper_bound() 函数用于在指定范围内查找大于目标值的第一个元素
//查找第一个大于7的值
int *p2 = upper_bound(a, a + 5, 7);
cout << "第一个大于7的值是:" << *p2 << endl;
//查找符合规则的元素集
auto it2 = upper_bound(vt.begin(), vt.end(), 7, comp());
cout << "第一个符合规则的值是:" << *it2 << endl;
int a2[] = {2, 5, 5, 5, 7, 5, 10};
//equel_range() 函数用于在指定范围内查找等于目标值的所有元素。
//从数组中查找元素5
pair<int *, int *> range = equal_range(a2, a2 + 9, 5);
cout << "查找到的序列集合:";
for (int *i = range.first; i != range.second; ++i) {
cout << *i << ",";
}
cout << endl;
vector<int> vt2{1, 6, 8, 22, 22, 22, 23, 28};
//binary_search() 函数用于查找指定区域内是否包含某个目标元素。
//从集合中查找元素23,查找成功返回true,失败返回false
bool exitEle = binary_search(vt2.begin(), vt2.end(), 23);
if (exitEle) cout << "元素已找到" << endl;
else cout << "元素未找到" << endl;
//匹配元素算法: all_of()所有值匹配、any_of()任意值匹配、none_of()没有匹配值匹配
/*all_of() 算法会返回 true,前提是序列中的所有元素都可以使谓词返回 true。
any_of() 算法会返回 true,前提是序列中的任意一个元素都可以使谓词返回 true。
none_of() 算法会返回 true,前提是序列中没有元素可以使谓词返回 true。*/
vector<int> arrs{22, 19, 46, 75, 54, 19, 27, 66, 61, 33, 22, 19};
int min_age = 22; //准备比对的元素
//all_of函数的逻辑是: 把容器中的所有元素均按照lambda表达式规则进行比较,
// 如果结果全部为true,则最终结果才返回true,否则返回false
bool all=all_of(arrs.begin(), arrs.end(), [min_age](int age) { return age < min_age; });
cout<< (all? "容器中的全部元素均小于22" :"容器中全部元素均不小于22")<< endl;
int any_ages = 22; //准备比对的元素
//any_of函数的逻辑是: 把容器中的所有元素均按照lambda表达式规则进行比较,
// 如果任意一个为true,则最终结果就返回true,否则返回false
bool any=any_of(arrs.begin(), arrs.end(), [any_ages](int age) { return age == any_ages; });
cout<< (any ? "容器中至少有一个等于22的元素" :"容器中没有等于22的元素")<< endl;
int any_age = 99; //准备比对的元素
//none_of函数的逻辑是: 把容器中的所有元素均按照lambda表达式规则进行比较,
// 如果每一个元素的比对结果都为false,则最终结果就返回true,否则返回false
bool none=none_of(arrs.begin(), arrs.end(), [any_age](int age) { return age == any_age; });
cout<< (none ? "容器中的元素均不等于99" :"容器中的元素均等于99")<< endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_45_findvalues.git
比较算法:
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
int main() {
vector<string> v1{"hello", "world", "hi", "type", "ios"};
vector<string> v2{"hello", "world", "ok", "type", "ios"};
//equal():如果两个序列的长度相同,并且对应元素都相等,equal() 算法会返回 true。
bool eq = equal(v1.begin(), v1.end(), v2.begin());
cout << (eq ? "相等" : "不相等") << endl;
//使用谓词函数自定义比较规则
bool eq2 = equal(v1.begin(), v1.end(), v2.begin(),
//只要第一个元素相等,那么集合的结果就相等!
[](const string &s1, const string &s2) { return s1 == s2; });
cout << (eq2 ? "相等" : "不相等") << endl;
/*mismatch() 返回的 pair 对象包含两个迭代器。它的 first 成员是一个来自前两个参数所指定
序列的迭代器,second 是来自于第二个序列的迭代器。当序列不匹配时,pair 包含的迭代器指向
第一对不匹配的元素;因此这个 pair 对象为 pair<iter1+n,iter2 + n>,这两个序列中索
引为 n 的元素是第一个不匹配的元素。
当序列匹配时,pair 的成员取决于使用的 mismatch() 的版本和具体情况。iter1 和
end_iter1 表示定义第一个序列的迭代器,iter2 和 end_iter2 表示第二个序列的开始和结束
迭代器。返回的匹配序列的 pair 的内容如下:
对于 mismatch(iter1,end_iter1,iter2):
返回 pair<end_iter1,(iter2 + (end_ter1 - iter1))>,pair 的成员 second 等于
iter2 加上第一个序列的长度。如果第二个序列比第一个序列短,结果是未定义的。
对于 mismatch(iterl, end_iter1, iter2, end_iter2):
当第一个序列比第二个序列长时,返回 pair<end_iter1, (iter2 + (end_iter1 - iter1))>
,所以成员 second 为 iter2 加上第一个序列的长度。
当第二个序列比第一个序列长时,返回 pair<(iter1 + (end_iter2 - iter2)),
end_iter2>, 所以成员 first 等于 iter1 加上第二个序列的长度。
当序列的长度相等时,返回 pair<end_iter1, end_iter2>。*/
pair<vector<string>::iterator, vector<string>::iterator> p;
p = mismatch(v1.begin(), v1.end(), v2.begin());
if (p.first != v2.end()) {
cout << "不同的元素是:" << *p.first << "," << *p.second << endl;
} else {
cout << "两个集合完全相同" << endl;
}
//lexicographical_compare() :按照字典顺序比较前一区间是否小于后一区间
bool compResult = lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end(),
[](const string &s1, const string &s2) {
return s1.length() < s2.length();
}
);
cout << (compResult ? "集合1的长度小于集合2的长度" : "集合1的长度不小于集合2的长度");
int count = 0;
//next_permutation() : 按照字典升序的方式生成的排列
// 获取一组数据的全排序列(穷举不同的排列方式,总的排列种树为元素的个数的阶乘)
//当到达最小序列时,next_permutation返回false,此时也就得到了集合的全部排列
do {
for (auto i = v1.begin(); i != v1.end(); ++i) {
cout << *i << ",";
}
count++;
cout << endl;
} while (next_permutation(v1.begin(), v1.end()));
cout << "一共有" << count << "种排列" << endl;
count = 0;
//按照字典降序的方式生成的排列
do {
for (auto i = v2.begin(); i != v2.end(); ++i) {
cout << *i << ",";
}
count++;
cout << endl;
} while (prev_permutation(v2.begin(), v2.end()));
cout << "一共有" << count << "种排列" << endl;
//is_permutation() 算法可以用来检查一个序列是不是另一个序列的排列,如果是,会返回 true
bool b = is_permutation(v1.begin(), v1.end(), v2.begin());
cout << (b ? "v1是v2的排序序列" : "v1不是v2的排序序列") << endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_46_compare.git
复制算法: 按照指定规则复制指定的序列集合元素
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
int main() {
vector<int> a{1, 2, 3};
vector<int> b{1};
//copy_n() 算法可以从源容器复制指定个数的元素到目的容器中。
copy_n(a.begin(), 2, inserter(b, b.begin())); //从集合a中复制2个元素到b集合中,位置为b的开头插入
cout << "b中有" << b.size() << "个元素" << endl;
//copy_n()的复制对象也可以是输入输出流,ostream_iterator<int>(cout,",")含义是复制到输出流中,使用,号分割元素
copy_n(b.begin(), b.size() - 1, ostream_iterator<int>(cout, ",")); //输出结果是: 1,2,
int len = 2;
//copy_if() 算法可以从源序列复制使谓词返回 true 的元素,相当于一个过滤器
copy_if(a.begin(), a.end(), inserter(a, a.begin()),
[len](const int &cs) { return cs < len; });
cout << "a中元素个数为:" << a.size() << endl;
//copy_backward() 在指定的区间中反向复制元素(从后到前)
copy_backward(a.begin(), a.end(), b.end());
cout << "b中元素个数为:" << b.size() << endl;
//reverse_copy() 算法可以将源序列复制到目的序列中,目的序列中的元素是逆序的。
reverse_copy(a.begin(), a.end(), b.begin());
cout << "b中元素个数为:" << b.size() << endl;
//unique() 算法可以在序列中原地移除重复的元素,但是被处理的序列是正向迭代器所指定的。
vector<string> words {"one", "two", "two", "three", "two", "two", "two"};
auto end_iter = unique(begin(words), end(words));
copy(begin(words), end_iter, ostream_iterator<string>{cout, " "});
cout << endl;
//rotate() 算法会从左边选择序列的元素
std::vector<string> words2 { "one", "two", "three", "four", "five","six", "seven", "eight"};
auto iter = std::rotate(std::begin(words2), std::begin(words2)+3, std::end(words2));
std::copy(std::begin(words2), std::end(words2),std::ostream_iterator<string> {std::cout, " "});
std::cout << std::endl << "First element before rotation: " << *iter << std::endl;
//rotate_copy() 算法会在新序列中生成一个序列的旋转副本,并保持原序列不变
std::vector<string> words3 {"one", "two", "three", "four", "five","six", "seven", "eight", "nine","ten"};
auto start = std::find(std::begin(words3), std::end(words3), "two");
auto end_iter2 = std::find (std::begin(words3) , std::end (words3) ,"eight");
std::vector<string> words_copy;
std::rotate_copy(start, std::find(std::begin(words3), std::end(words3),"five") , end_iter2, std::back_inserter (words_copy));
std::copy(std::begin(words_copy), std::end(words_copy),std::ostream_iterator<string> {std::cout, " "});
std::cout << std::endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_47_copy.git
移动算法:按照指定规则移动元素序列
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
#include <utility>
using namespace std;
int main() {
vector<int> a{1, 2, 3, 4};
deque<int> b{5, 6, 7, 8,1,2,3,99,8};
//move() :将指定的元素集合移动到指定的序列中
move(begin(a), end(a), back_inserter(b));
cout << "b---size: " << b.size() << endl;
//swap_ranges() :交换两个序列
swap_ranges(a.begin(), a.end(), b.begin());
//输出交换后的集合
for (auto i = a.begin(); i != a.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//remove() : 移除指定元素
remove(a.begin(),a.end(),5);
//输出移除后的集合
for (auto i = a.begin(); i != a.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//remove_copy():保留原始序列,并生成一个移除选定元素之后的副本
deque<double> s {1.5, 2.6, 0.0, 3.1, 0.0, 0.0, 4.1, 0.0, 6.7, 0.0};
vector<double> es;
remove_copy(begin(s), end(s), back_inserter(es), 0.0);
//输出移除后的集合
for (auto i = es.begin(); i != es.end(); ++i) {
cout << *i << ",";
}
cout << endl;
int ele=99;
/*remove_if() 提供了更强大的能力,它能够从序列中移除和给定值匹配的元素。
谓词会决定一个元素是否被移除;它接受序列中的一个元素为参数,并返回一个布尔值。*/
remove_if(a.begin(),a.end(),[ele](const int& cs){ return ele==cs;});
//输出移除后的集合
for (auto i = b.begin(); i != b.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//remove_copy_if() :保存移除后的结果,不对原数据集做更更改
remove_copy_if(a.begin(),a.end(), back_inserter(b),[ele](const int& cs){ return ele==cs;});
//输出移除后的集合
for (auto i = b.begin(); i != b.end(); ++i) {
cout << *i << ",";
}
cout << endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_48_move.git
填充与替换算法: 按照指定的规则填充或替换数据集合中的指定元素。
#include <iostream>
#include <algorithm>
#include <vector>
#include <deque>
using namespace std;
int main() {
vector<int> ve(10);
vector<int> ve2(10);
//fill() 填充整个序列
fill(ve.begin(), ve.end(), 9); //全部给集合填充值设置为9
for (auto i = ve.begin(); i != ve.end(); ++i) {
cout << *i << ",";
}
cout << endl;
fill_n(ve.begin(), 3, 99); //在集合开头处连续填充3个99
for (auto i = ve.begin(); i != ve.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//generate(): 以指定动作运算结果填充特定范围内的元素内容
generate(ve.begin(), ve.begin() + 1, []() { return 999; }); //在集合0的位置填充999
for (auto i = ve.begin(); i != ve.end(); ++i) {
cout << *i << ",";
}
cout << endl;
// generate_n() :以指定动作的运算结果填充n个元素内容
generate(ve.begin(), ve.begin() + 5, []() { return 111; }); //从集合开头处连续填充5个111
for (auto i = ve.begin(); i != ve.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//transform(): 在指定范围内应用于给定的操作,并将结果存储到指定的另一个范围内.
//把集合1的每一个元素增加100之后,依次放到集合2中去
transform(ve.begin(), ve.end(), ve2.begin(), [](int t) { return t + 100; });
for (auto i = ve2.begin(); i != ve2.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//replace() :使用新的值来替换和给定值相匹配的元素
replace(ve2.begin(), ve2.end(), 211, 119); //使用119来替换集合中所有为211的元素
for (auto i = ve2.begin(); i != ve2.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//replace_if() 会将使谓词返回 true 的元素替换为新的值。
string password{"This is a good choice !"};
//使用下划线代替空格
std::replace_if(std::begin(password), std::end(password), [](char ch) { return std::isspace(ch); }, '_');
cout << password << endl;
//replace_copy(): 把结果保存到另一个序列中,而不会改变原始序列。
std::vector<string> words { "one","none", "two", "three", "none", "four"};
std::vector<string> new_words;
std::replace_copy (std::begin (words), std::end(words),
std::back_inserter (new_words), string{"none"}, string{"0"});
for (auto i = new_words.begin(); i != new_words.end(); ++i) {
cout << *i << ",";
}
cout << endl;
//replace_copy_if(): 在序列中有选择地替换元素,结果保存到另一个序列中。
std::deque<int> data {10, -5, 12, -6, 10, 8, -7, 10,11};
std::vector<int> data_copy;
std::replace_copy_if(std::begin(data), std::end(data),
std::back_inserter(data_copy),[](int value) {return value == 10;}, 99);
for (auto i = data_copy.begin(); i != data_copy.end(); ++i) {
cout << *i << ",";
}
cout << endl;
return 0;
}
[email protected]:XiaoWang_csdn/cpp_49_fill.git
- 仿函数、
仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。
仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。
如果编程者要将某种“操作”当做算法的参数,一般有两种方法:
(1)一个办法就是先将该“操作”设计为一个函数,再将函数指针当做算法的一个参数。上面的实例就是该做法;
(2)将该“操作”设计为一个仿函数(就语言层面而言是个 class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数。
// less的定义
template<typename _Tp> struct less : public binary_function<_Tp, _Tp, bool> {
bool operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
// set 的申明
template<typename _Key, typename _Compare = std::less<_Key>,typename _Alloc = std::allocator<_Key>> class set;
实例代码:
class StringAppend {
public:
explicit StringAppend(const string& str) : ss(str){}
void operator() (const string& str) const {
cout << str << ' ' << ss << endl;
}
private:
const string ss;
};
int main() {
StringAppend myFunctor2("and world!");
myFunctor2("Hello");
}
- 适配器
利用已经存在的东西组合或者限制实现一个新的东西,这个新的东西具有某种新的性质,但是他的底层是由已经存在的东西实现的。
适配器是利用一种比较通用的数据结构来实现更加具体的,更加贴近实际应用的一种数据结构。
c++中的适配器有三种:容器适配器,迭代器适配器,函数适配器
容器适配器:具体的有stack,queue,priorty-queue,默认的情况如下:stack和queue是基于deque实现的。
priorty-queue是在vector上实现的,可以根据第二个实参指定容器的类型,但是一定要符合标准,queue要求有push_back的操作,因此不能建立在vector的基础上,priorty-queue要求有随机访问的功能,因此建立在vector上。
- 空间配置器。
template <int inst>
class __malloc_alloc_template
{
private:
static void *oom_malloc(size_t);
public:
// 对malloc的封装
static void * allocate(size_t n) {
// 申请空间成功,直接返回,失败交由oom_malloc处理
void *result = malloc(n);
if (0 == result)
result = oom_malloc(n);
return result;
}
// 对free的封装
static void deallocate(void *p, size_t /* n */)
{
free(p);
}
// 模拟set_new_handle
// 该函数的参数为函数指针,返回值类型也为函数指针
// void (* set_malloc_handler( void (*f)() ) )()
static void(*set_malloc_handler(void(*f)()))()
{
void(*old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return(old);
}
};
// malloc申请空间失败时代用该函数
template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n) {
void(*my_malloc_handler)();
void *result;
for (;;)
{
// 检测用户是否设置空间不足应对措施,如果没有设置,抛异常,模式new的方式
my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == my_malloc_handler)
{
__THROW_BAD_ALLOC;
}
// 如果设置,执行用户提供的空间不足应对措施
(*my_malloc_handler)();
// 继续申请空间,可能就会申请成功
result = malloc(n);
if (result)
return(result);
}
}
typedef __malloc_alloc_template<0> malloc_alloc;