全网最全!初学STL看这一篇就够了!
C++的一大主要目的是促进代码重用(减少程序员工作量),之前学习的公有继承、模板正是实现这种目的的机制之一。而STL的出现更是大大提高了代码复用率,对于学习C++,学习STL标准模板库是必不可少的,本文包含了STL基础的算法、容器及其成员函数和迭代器,为大家讲解STL相关知识,希望对于学习STL的同学有所帮助~~😃
文章目录
脑图
本文重点介绍:
-
标准C++ string类;
-
模板auto_ptr、unique_ptr和shared_ptr;
-
标准模板库(STL)
-
容器类
-
迭代器
-
函数对象(functor)
-
STL算法
-
模板initializer_list
标准c++ string类
有很多朋友们可能会发问,“string还用你教?”,错误的!C++中的字符串有两种,它们分别是C风格字符串(C-style string)和更现代和更安全的字符串string类。二者区别如下:
特性 | C风格字符串 | string类 |
---|---|---|
存储方式 | 以’\0’为结尾的字符数组 | 类,内部封装了一个字符数组 |
内存管理 | 手动 | 自动 |
安全性 | 容易出现空指针问题和缓冲区溢出问题 | 更加安全 |
效率 | 在某些情况下可能更有效 | 提供很多方便的操作方法,可以提高开发效率 |
使用场景 | 与C语言代码互操作,或者需要对内存进行精细控制 | 大多数其他情况 |
博主帮各位总结一下,C++更推崇支持的字符串类型是string类,而不是C风格字符串,但由于C语言是C++的亲爹,它支持亲爹的东西也是必然的。不过还是自己的东西用着舒服,string类应该被推崇为C++程序员的选择,因为它更加安全、易用、高效。
那么大家必然发问,“学STL学什么string类啊?”,错误的!后文要提到**“容器”的概念,string类不仅是C++标准库中提供的基础类**,它还就是此类容器之一。关于为什么我会先提到string类:
-
string是STL中最重要的容器之一。STL提供了很多用于操作容器的算法,而string是这些算法最常用的容器之一。例如,
std::copy()
、std::find()
、std::sort()
等算法都可以用于String容器。 -
string是STL中功能最强大的容器之一。String类提供了很多方便的操作方法,可以满足各种字符串操作需求。例如,
std::string::append()
、std::string::find()
、std::string::replace()
等方法可以用于对字符串进行各种操作。 -
string是STL中学习难度最小的容器之一。String类的接口设计非常直观,即使是初学者也可以很快地掌握如何使用它。
string类支持头文件
#include <string>
注意,string类由头文件string支持,头文件string.h和cstring支持对C风格字符串进行操纵的C库字符串函数,但不支持string类。注意不要打错咯~
构造字符串
string类是C++标准库中提供的用于表示字符串的类。string类提供了九个构造函数,用于创建新的string对象。
以下是string类的九个构造函数:
- 默认构造函数
string();
该构造函数创建一个空字符串,长度为0。
- 从C风格字符串构造
string(const char* s);
该构造函数将C风格字符串转换为string对象,将string对象初始化为s指向的NBTS(NBTS 是“以 null 结尾的字符串”的缩写。它是 C 语言中表示字符串的一种常见方式。NBTS 以 null 字符(‘\0’)结尾,null 字符表示字符串的结束。)。
- 从另一个string对象构造
string(const string& str);
该构造函数创建一个新的string对象,其内容与str相同。
- 从字符数组构造
string(const char* s, size_t n);
该构造函数创建一个新的string对象,其内容为s的前n个字符。
- 从字符重复构造
string(size_t n, char c);
该构造函数创建一个新的string对象,其内容为n个字符c。
- 从迭代器构造
template < class InputIt >
string(InputIt first, InputIt last);
该构造函数创建一个新的string对象,其内容为迭代器first到last之间元素的范围。
- 从initializer_list构造
string(initializer_list<char> il);
该构造函数创建一个新的string对象,其内容为initializer_list中的元素。
- 从移动语义的 string 对象构造
string(string && str) noexcept;
该构造函数将一个临时 string 对象的内容移动到新创建的 string 对象中。该函数不会抛出任何异常。它是C++11新增的构造函数,类似于复制构造函数,但它不保证将str视为const,因此这种构造函数被称为:移动构造函数。
- 从另一个 string 对象的子字符串构造
string(const string & str, size_type pos, size_type n = npos);
该构造函数从另一个 string 对象中构造一个新的 string 对象,并可以指定子字符串范围。
以下是string类的九个构造函数的示例:
// 默认构造函数
string str1;
// 从C风格字符串构造
string str2("Hello, world!");
// 从另一个string对象构造
string str3(str2);
// 从字符数组构造
string str4("Hello, world!", 5);
// 从字符重复构造
string str5(10, 'a');
// 从迭代器构造
std::vector<char> v = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'};
string str6(v.begin(), v.end());
// 从initializer_list构造
string str7{'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'};
// 从移动语义的 string 对象构造
string str8(std::move(str1));
// 从另一个 string 对象的子字符串构造
string str9(str2, 7, 5);
输出:
""
"Hello, world!"
"Hello, world!"
"Hello"
"aaaaaaaaaa"
"Hello, world!"
"Hello, world!"
""
"world"
需要注意的是,string类的构造函数可能会受到空格的影响。例如,使用C风格字符串构造string对象时,遇到空格就会停止构造。如果要构造包含空格的字符串,可以使用其他构造函数。
此外,string类的构造函数还会受到数据类型的限制。例如,使用initializer_list构造string对象时,initializer_list中的元素必须是char类型。如果要构造包含不同类型数据的字符串,可以使用其他构造函数。
string类输入
string类的输入共有两种
string stuff;
cin>>stuff; // 读入一个单词
getline(cin>>stuff); //读整行,直到'\n'
getline()函数的功能
1. 可用于指定哪个字符来确定输入的边界
getline()函数的第二个参数可以指定一个字符,该字符将作为输入的边界。当用户输入该字符时,getline()函数将停止读取并返回。例如,以下代码将读取用户输入的一行字符,直到用户输入换行符:
std::string str;
std::getline(std::cin, str);
以下代码将读取用户输入的一行字符,直到用户输入空格:
std::string str;
std::getline(std::cin, str, ' ');
2. 可自动调整目标string对象的大小,使之能正好储存输入的字符
getline()函数会自动调整目标string对象的大小,以容纳输入的字符。这意味着您不必预先知道要输入多少字符。例如,以下代码将读取用户输入的一行字符,并将它们存储在一个string对象中:
std::string str;
std::getline(std::cin, str);
即使用户输入了非常长的字符串,str对象也会自动调整大小以容纳所有字符。
3. 可用于从文件或其他输入流中读取数据
getline()函数不仅可以从标准输入流(std::cin)中读取数据,还可以从文件或其他输入流中读取数据。例如,以下代码将从一个名为“input.txt”的文件中读取一行字符:
std::ifstream infile("input.txt");
std::string str;
std::getline(infile, str);
示例
以下是一些使用getline()函数的示例:
1. 读取用户输入的姓名和年龄
std::string name;
std::cout << "请输入您的姓名:";
std::getline(std::cin, name);
int age;
std::cout << "请输入您的年龄:";
std::cin >> age;
std::cout << "您的姓名是:" << name << ", 年龄是:" << age << std::endl;
2. 读取文件中的所有行
std::ifstream infile("input.txt");
std::string line;
while (std::getline(infile, line)) {
std::cout << line << std::endl;
}
3. 读取用户输入的一行字符,并将其分割成单词
std::string str;
std::getline(std::cin, str);
std::vector<std::string> words;
std::stringstream ss(str);
std::string word;
while (ss >> word) {
words.push_back(word);
}
for (const std::string& word : words) {
std::cout << word << std::endl;
}
string类的使用(对象运算符的使用和一些主要方法)
- string 对象运算符的使用
1. 加法运算符 (+)
加法运算符 (+) 可用于将两个 string 对象连接在一起。例如:
std::string str1 = "Hello, ";
std::string str2 = "world!";
std::string str3 = str1 + str2;
// str3 包含 "Hello, world!"
2. 赋值运算符 (=)
赋值运算符 (=) 可用于将一个 string 对象的内容复制到另一个 string 对象中。例如:
std::string str1 = "Hello, world!";
std::string str2;
str2 = str1;
// str2 包含 "Hello, world!"
3. 比较运算符 (<, <=, >, >=, ==, !=)
比较运算符 (<, <=, >, >=, ==, !=) 可用于比较两个 string 对象的内容。例如:
std::string str1 = "Hello, world!";
std::string str2 = "Hello, world!";
if (str1 == str2) {
std::cout << "str1 和 str2 相等" << std::endl;
} else {
std::cout << "str1 和 str2 不相等" << std::endl;
}
4. 下标运算符 ([])
下标运算符 ([]) 可用于访问 string 对象中指定位置的字符。例如:
std::string str = "Hello, world!";
char c = str[0]; // c 等于 'H'
5. 范围运算符 (begin(), end())
范围运算符 (begin(), end()) 可用于获取 string 对象的迭代器范围。该范围可用于遍历 string 对象中的所有字符。例如:
std::string str = "Hello, world!";
for (auto it = str.begin(); it != str.end(); ++it) {
std::cout << *it;
}
6. 其他运算符
string 对象还支持其他一些运算符,例如:
-
+= 运算符:将一个 string 对象的内容追加到另一个 string 对象中。
-
+= 运算符:将一个字符追加到 string 对象中。
-
主要方法
string 对象除了支持丰富的运算符之外,还提供了许多有用的方法,可用于执行各种操作。以下是 string 对象的一些主要方法:
1. size() 方法
size() 方法用于获取 string 对象的长度,即包含的字符数。例如:
std::string str = "Hello, world!";
std::size_t len = str.size(); // len 等于 13
std::cout << len << std::endl; // 输出:13
2. capacity() 方法
capacity() 方法用于获取 string 对象的容量,即可以容纳的最大字符数。例如:
std::string str = "Hello, world!";
std::size_t capacity = str.capacity(); // capacity 等于 15
std::cout << capacity << std::endl; // 输出:15
3. empty() 方法
empty() 方法用于判断 string 对象是否为空。例如:
std::string str = "";
if (str.empty()) {
std::cout << "str 是空字符串" << std::endl;
} else {
std::cout << "str 不是空字符串" << std::endl;
}
4. clear() 方法
clear() 方法用于清空 string 对象的内容。例如:
std::string str = "Hello, world!";
str.clear();
std::cout << str << std::endl; // 输出:
5. c_str() 方法
c_str() 方法用于获取 string 对象的 C 风格字符串表示形式。例如:
std::string str = "Hello, world!";
const char* c_str = str.c_str();
std::cout << c_str << std::endl; // 输出:Hello, world!
6. find() 方法
find() 方法用于查找 string 对象中指定子字符串的位置。例如:
C++
std::string str = "Hello, world!";
std::size_t pos = str.find("world"); // pos 等于 7
std::cout << pos << std::endl; // 输出:7
7. substr() 方法
substr() 方法用于从 string 对象中提取子字符串。例如:
std::string str = "Hello, world!";
std::string substring = str.substr(7); // substring 等于 "world!"
std::cout << substring << std::endl; // 输出:world!
8. append() 方法
append() 方法用于将指定字符串追加到 string 对象的末尾。例如:
std::string str = "Hello, ";
str.append("world!");
std::cout << str << std::endl; // 输出:Hello, world!
9. replace() 方法
replace() 方法用于替换 string 对象中指定的子字符串。例如:
std::string str = "Hello, world!";
str.replace(7, 5, "!");
std::cout << str << std::endl; // 输出:Hello, !
10. 其他方法
string 对象还提供了许多其他方法,例如:
- compare() 方法:比较两个 string 对象的内容。
- copy() 方法:将 string 对象的内容复制到另一个 string 对象中。
- erase() 方法:从 string 对象中删除指定子字符串。
- insert() 方法:在 string 对象中插入指定字符串。
- lowercase() 方法:将 string 对象中的所有字符转换为小写。
- uppercase() 方法:将 string 对象中的所有字符转换为大写。
智能指针模板类
啥是智能指针?顾名思义,就是比普通指针更为高级智能的指针,原始的指针不智能在哪里?智能指针又牛在哪里?工作原理又是什么?本段重点介绍三个智能指针:shared_ptr、unique_ptr、 auto_ptr以及它们之间的区别。
智能指针模板的必要性和用处
为什么要引出智能指针模板?
在 C++ 中,使用原始指针(raw pointer)来管理动态内存可能会导致一些问题,例如:
- 内存泄漏: 当不再需要某个对象时,忘记释放其内存会导致内存泄漏。
- 悬垂指针: 当指向对象的指针被销毁后,仍然存在指向该对象的指针,称为悬垂指针。
- 野指针: 指向不存在对象的指针,称为野指针。
智能指针模板旨在解决这些问题,并提供一种更安全、更有效的方式来管理动态内存。
智能指针模板的用处:
智能指针模板可以自动释放内存,避免内存泄漏、悬垂指针和野指针等问题。它还可以简化代码,提高代码的健壮性。
通俗解释:
假设你正在开发一个游戏,游戏中需要创建很多对象,例如玩家、敌人、物品等。使用原始指针来管理这些对象可能会导致内存泄漏。例如,当你创建一个敌人对象时,你需要使用 new
关键字来分配内存。
Enemy* enemy = new Enemy();
但是,当你不再需要这个敌人对象时,你需要手动释放其内存。
delete enemy;
如果忘记释放内存,就会导致内存泄漏。
智能指针模板可以自动释放内存,避免这种情况发生。例如,你可以使用 std::shared_ptr
来创建敌人对象。
std::shared_ptr<Enemy> enemy(new Enemy());
std::shared_ptr
会自动跟踪指向敌人的指针数量。当所有指针都失效时,std::shared_ptr
会自动释放内存。
以下是一些使用智能指针模板的示例:
- 在游戏中管理对象
- 在数据库应用程序中管理连接
- 在网络应用程序中管理套接字
- 在任何需要管理动态内存的地方
shared_ptr
什么是 shared_ptr?
shared_ptr 是 C++ 中一种智能指针,用于管理动态内存。它可以自动释放内存,避免内存泄漏。
shared_ptr 的工作原理
shared_ptr 使用引用计数来跟踪指向同一对象的指针数量。当所有指针都失效时,shared_ptr 会自动释放内存。
shared_ptr 的优点
- 自动释放内存,避免内存泄漏
- 可以让代码更简洁
- 线程安全
shared_ptr 的使用示例
1. 朋友共享餐厅账单
假设你和朋友去餐厅吃饭,你们决定共享账单。你可以使用 shared_ptr 来表示账单对象:
#include <memory>
std::shared_ptr<Bill> bill(new Bill());
// 你和朋友都可以访问账单
bill->add_item("老八巨无霸", 10);
bill->add_item("老八小汉堡", 2);
// 你们吃完饭后,你们都可以支付账单
if (you_pay()) {
bill->pay();
} else {
your_friend_pay();
}
在这个例子中,bill
是一个 shared_ptr 指针,指向一个 Bill
对象。你和朋友都可以访问和修改账单。当你们吃完饭后,你们都可以支付账单。无论谁支付账单,shared_ptr 都会确保内存被正确释放。
2. 老师和学生共享书籍
假设老师有一本书,他想让学生共享这本书。可以使用 shared_ptr 来表示书籍对象:
#include <memory>
std::shared_ptr<Book> book(new Book("C++高级编程"));
// 老师和学生都可以阅读这本书
teacher->read_book(book);
student->read_book(book);
// 老师完成阅读后,他将书归还给图书馆
teacher->return_book(book);
// 学生完成阅读后,他也将书归还给图书馆
student->return_book(book);
在这个例子中,book
是一个 shared_ptr 指针,指向一个 Book
对象。老师和学生都可以阅读这本书。当老师完成阅读后,他将书归还给图书馆。当学生完成阅读后,他也将书归还给图书馆。无论谁最后归还书籍,shared_ptr 都会确保内存被正确释放。
由此看出,shared_ptr 是一种非常有用的智能指针,可以帮助你避免内存泄漏并简化代码。它适用于任何需要共享对象所有权的情况。
unique_ptr
什么是 unique_ptr?
unique_ptr 是 C++ 中一种智能指针,用于管理动态内存。它可以自动释放内存,避免内存泄漏。
unique_ptr 的工作原理
unique_ptr 只能有一个(1个!!这是它与shared_ptr最大的区别)指向所管理对象的指针。unique_ptr 无法复制或赋值,只能移动。
unique_ptr 的优点
- 自动释放内存,避免内存泄漏
- 可以防止意外共享对象(在设置权限问题时十分有用~选他选他)
- 可以让代码更简洁
unique_ptr 的使用示例
1. 工厂生产产品
假设一个工厂生产产品。每个产品都是唯一的,不能被共享。可以使用 unique_ptr 来表示产品对象:
#include <memory>
std::unique_ptr<Product> product(new Product());
// 产品只能由工厂生产
// ...
// 产品完成使用后,自动销毁
在这个例子中,product
是一个 unique_ptr 指针,指向一个 Product
对象。产品只能由工厂生产。产品完成使用后,自动销毁。
2. 临时对象
有时我们需要创建临时对象,并在使用后立即销毁它们。可以使用 unique_ptr 来表示临时对象:
#include <memory>
std::unique_ptr<std::stringstream> ss(new std::stringstream());
// 使用 ss 进行一些操作
// ...
// ss 完成使用后,自动销毁
在这个例子中,ss
是一个 unique_ptr 指针,指向一个 std::stringstream
对象。ss
用于执行一些操作。ss
完成使用后,自动销毁。
总结
unique_ptr 是一种非常有用的智能指针,可以帮助你避免内存泄漏并简化代码。它适用于任何需要确保对象唯一所有权的情况。
注意事项
- unique_ptr 不是线程安全的。(unique_ptr 使用原子计数器来跟踪指向所管理对象的指针数量。当所有指针都失效时,unique_ptr 会自动释放内存。在多线程环境中,多个线程可能会同时访问 unique_ptr 指针。如果多个线程同时尝试修改原子计数器,可能会导致数据竞争。数据竞争可能导致程序崩溃或其他意外行为。)P.S博主在这里也只是不知其所以然,懂得的大佬麻烦沟通交流!(●’◡’●)~~
- 在使用 unique_ptr 之前,你需要确保你理解它的语义。
auto_ptr
什么是 auto_ptr?
auto_ptr 是 C++ 11 之前标准库中提供的智能指针类型。它已经被 shared_ptr 和 unique_ptr 取代。
auto_ptr 的主要特点:
- 类似于 unique_ptr,只能有一个指向所管理的对象的指针。
- 自动释放内存:当 auto_ptr 超出作用域时,对象将被自动释放。
auto_ptr 的使用示例
#include <memory>
std::auto_ptr<int> ptr(new int(10));
// ptr 指向一个值为 10 的 int 对象
// ...
// ptr 超出作用域,int 对象被自动释放
注意事项:
-
一般情况下,建议使用 shared_ptr 或 unique_ptr,而不是 auto_ptr。
不建议使用 auto_ptr 的主要原因如下:
- 语义不明确: auto_ptr 的赋值语义和拷贝语义不明确,容易导致意外行为。
- 缺乏移动语义: auto_ptr 不支持移动语义,导致效率低下。
- 非线程安全: auto_ptr 不是线程安全的,在多线程环境中使用可能会导致数据竞争。
-
auto_ptr 不是线程安全的。
三种智能指针的比较
特性 | auto_ptr | shared_ptr | unique_ptr |
---|---|---|---|
可复制 | 否 | 是 | 否 |
可移动 | 是 | 是 | 是 |
线程安全 | 否 | 是 | 否 |
使用场景 | 不建议使用 | 共享所有权 | 唯一所有权 |
STL标准模板库
终于!在了解了String类和智能指针后,我们开始了STL的重点学习!只要你掌握了模板类,就可以随心所欲地使用STL模板库了!大家兴奋起来了吗,博主好兴奋啊哈哈哈哈哈哈哈哈哈,让大家一起快乐的学习吧!(_)
STL为什么被创建(作用)
软件界追求提高代码复用性,但数据结构与算法没有一套标准,这时候,STL相应诞生。(人话:C++的“PPT模板”,大家都用系统提供的模板,少干事)。STL不是追求OOP面向对象对象编程的理念,而是泛型编程。
STL的概念
-
STL(Standard Template Libray,标准模板库)
-
STL从广义上划分:容器(container)、算法(algorithm)、迭代器(iterator)
-
迭代器作为桥梁。联系容器和算法。
STL六(四)大组件
**容器(Contrainers)、算法(Algorithms)、迭代器(Iterators)、仿函数(Functors)、**适配器(配接器)、空间配置器
博主看国外的教材一般是划分为四个大组件,这也足够说明后两者不是我们的学习重点。
各自的作用:
-
容器:各种数据结构,如vector、list、deque、 set、 map等用来存放数据。(后文中会具体介绍)
-
算法:各种常用的算法,如sort、 find、 copy. for_ each等
-
迭代器:扮演了容器与算法之间的胶合剂。
-
仿函数:行为类似函数,可作为算法的某种策略。
-
适配器: - -种用来修饰容器或者仿函数或迭代器接口的东西。
-
空间配置器:负责空间的配置与管理。
算法
让我们先从算法开始!因为算法是理解STL的基础。STL的数据结构是基于算法来实现的。算法的内容非常丰富,在这里我们只介绍Sorting、Searching和Copy两个最重要的算法。
标头算法定义了专门设计用于一系列元素的函数集合。它们作用于容器并提供对容器内容物进行各种操作的手段。
算法头文件
#include <algorithm>
这是算法的头文件,使用算法时记得加上!!
Sorting
sorting是应用于数据的最基本功能之一。这意味着以特定的方式排列数据,可以增加或减少。C++ STL 中有一个名为 sort() 的内置函数。
简介
sort()
函数是 STL 标准库中提供的排序算法之一,用于对容器中的元素进行排序。它可以对各种类型的容器进行排序,例如数组、vector、list等。
语法
#include <algorithm>
// 对容器中的元素进行排序
template <class T>
void sort(T begin, T end);
// 使用自定义比较函数进行排序
template <class T, class Compare>
void sort(T begin, T end, Compare comp);
参数说明
begin
: 指向容器第一个元素的迭代器。end
: 指向容器最后一个元素的迭代器。comp
: 可选参数,指向自定义比较函数的指针。
返回值
无。
排序规则
- 默认情况下,
sort()
函数会按照升序对容器中的元素进行排序。 - 如果需要按照降序进行排序,可以使用自定义比较函数。
自定义比较函数
自定义比较函数用于指定排序规则。它接受两个参数,分别代表待比较的两个元素。如果第一个参数小于第二个参数,则返回 true;否则返回 false。
示例
#include <algorithm>
#include <iostream>
int main() {
int numbers[] = {5, 2, 4, 6, 1, 3};
// 使用 sort() 函数对数组进行排序
std::sort(numbers, numbers + 6);
// 打印排序后的数组
for (int i = 0; i < 6; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
1 2 3 4 5 6
示例:使用自定义比较函数进行降序排序
#include <algorithm>
#include <iostream>
bool compare(int a, int b) {
return a > b; // 降序排列
}
int main() {
int numbers[] = {5, 2, 4, 6, 1, 3};
// 使用 sort() 函数和自定义比较函数对数组进行排序
std::sort(numbers, numbers + 6, compare);
// 打印排序后的数组
for (int i = 0; i < 6; i++) {
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
return 0;
}
输出:
6 5 4 3 2 1
注意事项
sort()
函数会改变容器中元素的顺序。- 如果容器中包含指针,则排序后指针指向的元素可能发生变化。
总结
sort()
函数是 STL 中常用的排序算法,可以对各种类型的容器进行排序。
Searching
简介
Searching算法是用于在数据结构中查找特定元素或满足特定条件的元素的算法。STL 标准库中提供了多种搜索算法,可以用于各种类型的容器,例如数组、vector、list等。
基本概念
- 线性搜索:从容器的第一个元素开始,逐个检查每个元素,直到找到目标元素或到达容器末尾。
- 二分搜索:适用于已排序的容器。将容器划分为两个子区间,然后根据目标元素与子区间中元素的比较结果,不断缩小搜索范围,直到找到目标元素或确定目标元素不存在。
STL 中的搜索算法
- find(): 在容器中查找指定元素。
- find_if(): 在容器中查找满足条件的元素。
- count(): 统计容器中满足条件的元素个数。
- count_if(): 统计容器中满足条件的元素个数。
- lower_bound(): 查找第一个大于或等于指定元素的元素。
- upper_bound(): 查找第一个大于指定元素的元素。
equal_range(): 查找指定元素在容器中的范围。
下面依次介绍:
1. find() 函数示例
#include <algorithm>
#include <iostream>
int main() {
int numbers[] = {1, 3, 5, 7, 9};
// 使用 find() 函数在数组中查找元素 5
auto it = std::find(numbers, numbers + 5, 5);
if (it != numbers + 5) {
std::cout << "找到元素 5,位置:" << (it - numbers) << std::endl;//it是位置,numbers是指向数组第一个元素的迭代器
} else {
std::cout << "未找到元素 5" << std::endl;
}
return 0;
}
输出:
找到元素 5,位置:2
2. find_if() 函数示例
#include <algorithm>
#include <iostream>
bool isEven(int x) {
return x % 2 == 0;
}
int main() {
int numbers[] = {1, 3, 5, 7, 9};
// 使用 find_if() 函数在数组中查找偶数
auto it = std::find_if(numbers, numbers + 5, isEven);
if (it != numbers + 5) {
std::cout << "找到第一个偶数,位置:" << (it - numbers) << std::endl;
} else {
std::cout << "未找到偶数" << std::endl;
}
return 0;
}
输出:
找到第一个偶数,位置:1
3. count() 函数示例
#include <algorithm>
#include <iostream>
int main() {
int numbers[] = {1, 3, 5, 7, 9, 1, 3};
// 使用 count() 函数统计数组中元素 1 的个数
int count = std::count(numbers, numbers + 7, 1);
std::cout << "元素 1 出现的次数:" << count << std::endl;
return 0;
}
输出:
元素 1 出现的次数:2
4. lower_bound() 函数示例
#include <algorithm>
#include <iostream>
int main() {
int numbers[] = {1, 3, 5, 7, 9};
// 使用 lower_bound() 函数查找第一个大于或等于 4 的元素
auto it = std::lower_bound(numbers, numbers + 5, 4);
if (it != numbers + 5) {
std::cout << "第一个大于或等于 4 的元素: " << *it << std::endl;
} else {
std::cout << "未找到大于或等于 4 的元素" << std::endl;
}
return 0;
}
输出:
第一个大于或等于 4 的元素: 5
5. upper_bound() 函数示例
#include <algorithm>
#include <iostream>
int main() {
int numbers[] = {1, 3, 5, 7, 9};
// 使用 upper_bound() 函数查找第一个大于 4 的元素
auto it = std::upper_bound(numbers, numbers + 5, 4);
if (it != numbers + 5) {
std::cout << "第一个大于 4 的元素: " << *it << std::endl;
} else {
std::cout << "未找到
copy
简介
std::copy()
算法用于将一个范围内的元素复制到另一个范围中。该算法接受以下参数:
first
: 源范围的第一个元素的迭代器。last
: 源范围的最后一个元素的迭代器。result
: 目标范围的第一个元素的迭代器。
std::copy()
算法会将源范围内的元素逐个复制到目标范围内,直到复制到最后一个元素。目标范围的大小必须至少等于源范围的大小。
示例
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
int numbers[] = {1, 2, 3, 4, 5};
std::vector<int> vec;
// 使用 copy() 函数将数组中的元素复制到 vector 中
std::copy(numbers, numbers + 5, std::back_inserter(vec));
for (int number : vec) {
std::cout << number << " ";
}
std::cout << std::endl;
return 0;
}
注意事项
std::copy()
算法不会改变源范围和目标范围的元素顺序。- 如果目标范围的大小小于源范围的大小,则会导致目标范围的元素被覆盖。
- 如果目标范围和源范围重叠,则会导致目标范围的元素被复制到自身。
变体算法
std::copy_backward()
:将源范围内的元素从后往前复制到目标范围内。std::copy_if()
:将满足条件的元素从源范围复制到目标范围内。std::uninitialized_copy()
:将源范围内的元素复制到目标范围内,目标范围不需要预先分配空间。
总结
std::copy()
算法是 STL 中常用的复制算法,可以用于将一个范围内的元素复制到另一个范围中。该算法易于使用,并且可以用于各种类型的容器。
容器类与成员函数
接下来咱们就来到容器和他的好基友迭代器了,其实我们对他们一点都不陌生,忘记了我们最开始的String类没有,它就是容器。
基本概念
容器
容器是 C++ 标准库中用于存储和组织数据的类模板。它们提供了一种统一的接口来访问和操作不同类型的数据。
常见容器类型:
-
顺序容器:
vector
、deque
、list
-
关联容器:
map
、set
-
无序容器:
unordered_map
、unordered_set
迭代器
迭代器是一种指针类型,用于遍历容器中的元素。它提供了一种与容器无关的方式来访问和修改元素。
迭代器类型:
- 输入迭代器:只能用于读取元素
- 输出迭代器:只能用于写入元素
- 前向迭代器:可以用于读取和写入元素,但只能向前移动
- 双向迭代器:可以用于读取和写入元素,并可以向前和向后移动
- 随机访问迭代器:可以用于读取和写入元素,并可以随机访问容器中的任何元素
)
容器与迭代器的关系
容器和迭代器是 C++ 标准库中两个密切相关的概念。容器用于存储数据,迭代器用于访问和操作数据。
使用迭代器遍历容器:
-
使用
begin()
和end()
成员函数获取容器的开头和结尾迭代器 -
使用
++
运算符将迭代器递增到下一个元素 -
使用
*
运算符解引用迭代器以获取元素值
由于各个容器有各自的特性以及使用多种迭代器,因此我们将他们分开介绍:
vector容器
vector基本概念
功能:
-
vector是一种序列容器(实现可以按顺序访问的数据结构。)
-
vector数据结构与数组非常相似,因此也被称为单端数组
-
向量与动态数组相同,能够在插入或删除元素时自动调整自身大小,其存储由容器自动处理。向量元素被放置在连续的存储中,以便可以使用迭代器访问和遍历它们。在向量中,数据插入到末尾。在末尾插入需要不同的时间,因为有时可能需要扩展数组。删除最后一个元素只需要恒定的时间,因为不会发生大小调整。在开头或中间插入和擦除在时间上是线性的。
vector与普通数组的区别:
- 数组是静态空间,而vector可以动态扩展
动态扩展:
- 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将元数据拷贝新空间,释放新空间
C++ 中的 std::vector 是什么?
C++ 中的std::vector是包含向量容器及其成员函数的类模板。它在头文件中定义。std::vector 类的成员函数为向量容器提供了各种功能。
头文件
#include <vector>
在 C++ 中声明vector的语法
std::vector <数据类型> 向量名称;
其中数据类型是向量每个元素的数据类型。如果您已经使用了 std 命名空间,则可以删除 std::。
C++中Vector的初始化
- 列表初始化
语法:
std::vector<dataType> {value1, value2, value3, ..., valueN};
示例:
std::vector<int> vec1 {1, 2, 3, 4, 5};
std::vector<std::string> vec2 {"Hello", "World"};
特点:
- 可以初始化任意类型的向量
- 元素可以是任意表达式
- 初始化顺序与元素在向量中的顺序相同
- 单一值初始化
语法:
std::vector<dataType> size(num(int),value);
示例:
std::vector<int> vec1(5, 0); // 初始化 5 个值为 0 的 int 型元素
std::vector<std::string> vec2(10, "Hello"); // 初始化 10 个值为 "Hello" 的 string 型元素
特点:
- 可以初始化任意类型的向量
- 所有元素都初始化为相同的值
3. 拷贝初始化
语法:
std::vector<dataType> vec(other_vec);
示例:
std::vector<int> vec1 {1, 2, 3, 4, 5};
std::vector<int> vec2(vec1); // vec2 是 vec1 的精确副本
特点:
- 创建一个新的向量,该向量包含与另一个向量相同元素的副本
- 两个向量拥有不同的内存空间
4. 其他初始化方式(后文介绍~)
- 使用
std::initializer_list
- 使用
std::fill
算法 - 使用
std::copy
算法
vector类的迭代器相关成员函数
begin()
– 返回一个指向向量中第一个元素的迭代器。begin() 函数返回一个指向容器第一个元素的双向迭代器。
例子:
// INTEGER VECTOR EXAMPLE
// CPP program to illustrate
// Implementation of begin() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// declaration of vector container
vector<int> myvector{ 1, 2, 3, 4, 5 };
// using begin() to print vector
for (auto it = myvector.begin();
it != myvector.end(); ++it)
cout << ' ' << *it;
return 0;
}
输出:
1 2 3 4 5
end()
– 返回一个迭代器,指向向量中最后一个元素后面的理论元素,end() 函数返回一个双向迭代器。
例子:
// STRING VECTOR EXAMPLE
// CPP program to illustrate
// Implementation of end() function
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
// declaration of vector container
vector<string> myvector{ "临!", "时!",
"抱!","佛!","脚!" };
// using end() to print vector
for (auto it = myvector.begin(); it != myvector.end();
++it)
cout << ' ' << *it;
return 0;
}
输出:
临! 时! 抱! 佛! 脚!
rbegin()
– 返回指向向量中最后一个元素的反向迭代器(反向开始)。它从最后一个元素移动到第一个元素。它返回指向容器中最后一个元素的反向迭代器。
例子:
// CPP program to illustrate
// the vector::rbegin() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> v;
v.push_back(11);//在vector最后插入11
v.push_back(12);//在vector最后插入12
v.push_back(13);//在vector最后插入13
v.push_back(14);//在vector最后插入14
v.push_back(15);//在vector最后插入15
// prints all the elements
cout << "The vector elements in reverse order are:\n";
for (auto it = v.rbegin(); it != v.rend(); it++)
cout << *it << " ";
return 0;
}
输出:
The vector elements in reverse order are:
15 14 13 12 11
rend()
– 返回一个反向迭代器,指向向量中第一个元素之前的理论元素(被视为反向端)
例子:
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> v;
v.push_back(11);
v.push_back(12);
v.push_back(13);
v.push_back(14);
v.push_back(15);
cout << "The last element is: " << *v.rbegin();
// prints all the elements
cout << "\nThe vector elements in reverse order are:\n";
for (auto it = v.rbegin(); it != v.rend(); it++)
cout << *it << " ";
return 0;
}
输出:
The last element is: 15
The vector elements in reverse order are:
15 14 13 12 11
cbegin()
– 返回一个指向向量中第一个元素的常量迭代器。
-
迭代器指向向量的开头。
-
迭代器无法修改向量的内容。
例子
// CPP program to illustrate
// use of cbegin()
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> vec;
// 5 string are inserted
vec.push_back("first"); //在vector最后插入first
vec.push_back("second"); //在vector最后插入second
vec.push_back("third");//在vector最后插入third
vec.push_back("fourth");//在vector最后插入fourth
vec.push_back("fifth");//在vector最后插入fifth
// displaying the contents
cout << "Contents of the vector:" << endl;
for (auto itr = vec.cbegin();
itr != vec.end();
++itr)
cout << *itr << endl;
return 0;
输出:
Contents of the vector:
first
second
third
fourth
fifth
cend()
– 返回一个常量迭代器,指向向量中最后一个元素后面的理论元素。
- 迭代器指向向量的末尾元素。
- 迭代器无法修改向量的内容。
例子:
int main()
{
vector<string> vec;
// 5 string are inserted
vec.push_back("first");
vec.push_back("second");
vec.push_back("third");
vec.push_back("fourth");
vec.push_back("fifth");
// displaying the contents
cout << "Contents of the vector:" << endl;
for (auto itr = vec.cend() - 1;
itr >= vec.begin();
--itr)
cout << *itr << endl;
return 0;
}
输出:
Contents of the vector:
fifth
fourth
third
second
first
crbegin()
**–它返回一个 const_reverse_iterator 指向容器中的最后一个元素(即它的反向开始)。 **
例子:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<string> v{"minji","hanni","danielle","haerin"};
vector<string>::const_reverse_iterator itr=v.crbegin();
cout<<*itr;
return 0;
}
输出:
haerin
crend()
– 它是一个公共成员函数,返回一个指向第一个元素之前的元素的 const_reverse_iterator。
例子:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
vector<string>str{"minji","hanni","danielle","haerin"};
vector<string>::const_reverse_iterator itr=str.crend()-1;
std::cout<< *itr;
return 0;
}
输出:
minji
vector容量相关成员函数
size()
– 返回向量中的元素数量。
例子:
// CPP program to illustrate
// Implementation of size() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5 };
cout << myvector.size();
return 0;
}
输出:
5
max_size()
– 返回向量可以容纳的最大元素数。
例子:
// C++ program to illustrate the
// vector::max_size() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialize a vector
vector<int> vec;
// returns the max_size of vector
cout << "max_size of vector 1 = " << vec.max_size() << endl;
vector<int> vec1;
// returns the max_size of vector
cout << "max_size of vector 2 = " << vec1.max_size() << endl;
return 0;
}
输出:
max_size of vector 1 = 4611686018427387903
max_size of vector 2 = 4611686018427387903
capacity()
– 返回当前分配给向量的存储空间大小,以元素数量表示。(vector **::capacity()**函数是一个内置函数,它返回当前为向量分配的存储空间的大小,以元素表示。该容量不一定等于向量大小。它可以等于或更大,额外的空间可以适应增长,而无需在每次插入时重新分配。容量并不限制向量的大小。当此容量耗尽并且需要更多容量时,容器会自动扩展它(重新分配存储空间)。向量大小的理论限制由成员 max_size 给出。 )
例子:
// C++ program to illustrate the
// vector::capacity() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> v;
// 插入元素
for (int i = 0; i < 10; i++) {
v.push_back(i * 10);
}
cout << "The size of vector is " << v.size();
cout << "\nThe maximum capacity is " << v.capacity();
return 0;
}
输出:
The size of vector is 10
The maximum capacity is 16
capacity()
和 max_size()
之间的区别:
-
capacity()
返回当前分配给 vector 的存储空间的大小,而max_size()
返回 vector 允许的最大元素数。 -
capacity()
可以通过调用vector::reserve()
来显式更改,而max_size()
是只读的。 -
capacity()
通常比max_size()
小得多。
resize(n)
– 调整容器大小,使其包含“n”个元素。
empty()
– 返回容器是否为空。
例子:
// CPP program to illustrate
// Implementation of empty() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{};
if (myvector.empty())
{
cout << "True";
}
else {
cout << "False";
}
return 0;
}
输出:
Ture
Shrink_to_fit()
– 减少容器的容量以适应其大小,并销毁超出容量的所有元素。
例子:
// C++ program to illustrate
// the vector::shrink_to_fit()
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Initialized vector
vector<int> v(10);
for (int i = 0; i < 10; i++)
v[i] = i;
// Initial vector
cout << "Vector size initially: " << v.size();
cout << "\nVector elements are: ";
for (int i = 0; i < 10; i++)
cout << v[i] << " ";
// changes the size of the Vector
// but does not destroys the elements
v.resize(5);
cout << "\n\nVector size after resize(5): "
<< v.size();
cout << "\nVector elements after resize(5) are: ";
for (int i = 0; i < 10; i++)
cout << v[i] << " ";
// Shrinks to the size
// till which elements are
// destroys the elements after 5
v.shrink_to_fit();
cout << "\n\nVector size after shrink_to_fit(): "
<< v.size();
cout << "\nVector elements after shrink_to_fit() are: ";
for (int i = 0; i < 10; i++)
cout << v[i] << " ";
return 0;
}
输出:
Vector size initially: 10
Vector elements are: 0 1 2 3 4 5 6 7 8 9
Vector size after resize(5): 5
Vector elements after resize(5) are: 0 1 2 3 4 5 6 7 8 9
//,resize(5) 确实将 vector 的大小缩减为 5,删除了末尾 5 个元素。但是,resize() 不会立即缩减 vector 的容量。这意味着,即使 vector 中只有 5 个元素,其容量仍然可能为 10。
Vector size after shrink_to_fit(): 5
Vector elements after shrink_to_fit() are: 0 1 2 3 4 0 127889 0 0 0
vector::resize()
与 vector::shrink_to_fit()
的区别
特性 | resize() | shrink_to_fit() |
---|---|---|
目的 | 改变向量大小 | 缩减向量容量 |
参数 | 整数,代表期望的新大小 | 无 |
对大小的影响 | - 缩小时:删除末尾元素,减少大小。 - 扩充时:添加默认初始化元素,可能重新分配内存。 | 不影响大小 |
对容量的影响 | 可能重新分配内存 | 尝试缩减容量,可能失败 |
使用场景 | 需要改变向量中元素数量时 | 大幅缩减向量大小后,释放未使用内存时 |
注意事项 | - 缩减后容量可能不立即改变。 - 扩充时可能导致性能下降。 | - 可能无法成功缩减容量。 - 并非总是最佳的内存优化手段。 |
Reserve()
– 请求向量容量至少足以包含 n 个元素。(std::vector类提供了一个有用的函数保留,可以帮助用户指定向量的最小大小。它表明创建的向量可以至少存储指定数量的元素,而无需重新分配内存。)
例子:
/ CPP program to demonstrate use of
// std::vector::reserve
#include <chrono>
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using namespace std::chrono;
int main()
{
// No of characters
int N = (int)1e6;
vector<int> v1, v2;
// Reserve space in v2
v2.reserve(N);
auto start = high_resolution_clock::now();
for (int i = 0; i < N; ++i)
v1.push_back(i);
auto stop = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(stop - start);
cout << "Method I took " << duration.count() << " microseconds\n";
// Start filling up elements in v2
start = high_resolution_clock::now();
for (int i = 0; i < N; ++i)
v2.push_back(i);
stop = high_resolution_clock::now();
duration = duration_cast<microseconds>(stop - start);
cout << "Method II took " << duration.count()
<< " microseconds \n";
return 0;
}
输出:
Method I took 18699 microseconds
Method II took 16276 microseconds
get_allocator()
get_allocator() 用于分配内存块。它返回与容器关联的分配器对象的副本。
例子:
// C++ program to show working
// of get_allocator function
#include <iostream>
#include <vector>
using namespace std;
// Function for allocating
char* Allocate(vector<char> arr, int size)
{
// allocate space for size(s) elements
return arr.get_allocator().allocate(size);
}
void Construct(vector<char> arr,
char* point, int size)
{
for (int iter = 0; iter < size; ++iter)
// construct values in-place on the array:
arr.get_allocator().construct(&point[iter],
iter + 97);
}
// Function for Deallocating
void deAllocate(vector<char> arr,
char* point, int size)
{
for (int iter = 0; iter < size; ++iter)
arr.get_allocator().destroy(&point[iter]);
// free allocated memory
arr.get_allocator().deallocate(point, size);
}
// Driver code
int main()
{
vector<char> array;
char* pointer;
int size = 8;
pointer = Allocate(array, size);
Construct(array, pointer, size);
cout << "Array elements: ";
for (int iter = 0; iter < size; ++iter)
cout << pointer[iter] << " ";
deAllocate(array, pointer, size);
return 0;
}
输出:
Array elements: abcdefgh
Vector的元素访问相关函数
= 和[ ]
运算符"="
该运算符用于通过替换现有内容来将新内容分配给容器。
它还根据新内容修改大小。
例子:
// CPP program to illustrate
// Implementation of = operator
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector1{ 1, 2, 3 };
vector<int> myvector2{ 3, 2, 1, 4 };
myvector1 = myvector2;
cout << "myvector1 = ";
for (auto it = myvector1.begin(); it != myvector1.end(); ++it)
cout << ' ' << *it;
return 0;
}
输出:
myvector1= 3 2 1 4
运算符"[]"
该运算符用于引用运算符内给定位置处存在的元素。它与 at() 函数类似,唯一的区别是 at() 函数在位置不在向量大小范围内时会抛出超出范围的异常,而该运算符会导致未定义的行为。
例子:
// CPP program to illustrate
// Implementation of [] operator
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector;
myvector.push_back(3);
myvector.push_back(4);
myvector.push_back(1);
myvector.push_back(7);
myvector.push_back(3);
cout << myvector[3];
return 0;
}
输出:
7
二者的区别
vector::运算符= | vector::运算符[ ] | |
---|---|---|
1. | 它用于向容器分配新内容并替换其当前内容 | 它用于通过替换当前内容来将新内容分配给容器。 |
2. | 它的语法是-:向量&运算符= (const向量& x); | 它的语法是-:向量&运算符= (const向量& x); |
3. | 它需要两个参数 -:1.同类型的向量对象2.一个initializer_list 对象。 | 它需要两个参数 -:1.同类型的向量对象2.一个initializer_list 对象。 |
4. | 其复杂度是线性的。 | 其复杂度是线性的。 |
5. | **它在**头文件中定义。 | 它在 头文件中定义。 |
at() 和swap()
– at(g)返回对向量中位置“g”的元素的引用,at() 函数用于引用作为函数参数给出的位置处存在的元素。
例子:
// CPP program to illustrate
// Implementation of at() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector;
myvector.push_back(3);
myvector.push_back(4);
myvector.push_back(1);
myvector.push_back(7);
myvector.push_back(3);
cout << myvector.at(3);
return 0;
}
输出:
7
swap(),此函数用于将一个向量的内容与另一相同类型的向量交换,并且向量的大小可能不同
例子:
// CPP program
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec1{ 100, 100, 100 };
vector<int> vec2{ 200, 200, 200, 200, 200 };
vec1.swap(vec2);
cout << "The vec1 contains:";
for (int i = 0; i < vec1.size(); i++)
cout << ' ' << vec1[i];
cout << '\n';
cout << "The vec2 contains:";
for (int i = 0; i < vec2.size(); i++)
cout << ' ' << vec2[i];
cout << '\n';
return 0;
}
输出:
The vec1 contains: 200 200 200 200 200
The vec2 contains: 100 100 100
二者的不同
vector::at() | vector::swap() | |
---|---|---|
1. | 它用于返回对向量中位置 n 的元素的引用。 | 它用于将一个向量的元素与另一向量的元素交换。 |
2. | 其语法为-: reference at (size_type n); | 其语法为——: swap(vector&x); |
3. | 它只接受一个参数,即容器中元素的位置。 | 它只需要一个参数,即我们要交换的向量。 |
4. | 它的复杂性是恒定的。 | 它没有任何返回值。 |
5. | 它的迭代器有效性不会改变。 | 它的复杂性是恒定的。 |
front()和back()
–front() 返回对向量中第一个元素的引用
例子:
// CPP program to illustrate
// Implementation of front() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector;
myvector.push_back(3);
myvector.push_back(4);
myvector.push_back(1);
myvector.push_back(7);
myvector.push_back(3);
// Vector becomes 3, 4, 1, 7, 3
cout << myvector.front();
return 0;
}
输出:
3
back()– 返回对向量中最后一个元素的引用
例子:
// CPP program to illustrate
// Implementation of back() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector;
myvector.push_back(3);
myvector.push_back(4);
myvector.push_back(1);
myvector.push_back(7);
myvector.push_back(2);
// Vector becomes 3, 4, 1, 7, 2
cout << myvector.back();
return 0;
}
输出:
2
二者的区别
vector::front() | vector::back() | |
---|---|---|
1. | 它用于返回对向量中第一个元素的引用。 | 它用于返回对向量中最后一个元素的引用。 |
2. | 它的语法是-:矢量名.front(); | 它的语法是-:向量名称.back(); |
3. | 它适用于矢量的前(左)侧。 | 它适用于矢量的后(右)侧。 |
4. | 它不带任何参数。 | 它不带任何参数。 |
5. | 它的复杂性是恒定的。 | 它的复杂性是恒定的。 |
6. | 它的迭代器有效性不会改变。 | 它的迭代器有效性不会改变。 |
data()
– 返回一个指向向量内部使用的内存数组的直接指针,用于存储其拥有的元素。
例子:
// C++ program to demonstrate the
// vector::date() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialising vector
vector<int> vec = { 10, 20, 30, 40, 50 };
// memory pointer pointing to the
// first element
int* pos = vec.data();
// prints the vector
cout << "The vector elements are: ";
for (int i = 0; i < vec.size(); ++i)
cout << *pos++ << " ";
return 0;
}
输出:
The vector elements are: 10 20 30 40 50
Vector的修饰符相关函数
assign()
***vector:: assgin()*是 C++ 中的 STL,它通过替换旧值来为向量元素分配新值。如有必要,它还可以修改向量的大小。
例子:
// CPP program to demonstrate
// how to assign constant values to a vector
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> v;
v.assign(7, 100);
cout << "Size of first: "
<< int(v.size()) << '\n';
cout << "Elements are\n";
for (int i = 0; i < v.size(); i++)
cout << v[i] << endl;
return 0;
}
输出:
Size of first: 7
Elements are
100
100
100
100
100
100
100
push_back() 和pop_back()
Push_back() 函数用于将元素从后面推入向量中。新值被插入到向量的末尾,即当前最后一个元素之后,并且容器大小增加 1。
例子:
// CPP program to illustrate
// push_back() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5 };
myvector.push_back(6);
// Vector becomes 1, 2, 3, 4, 5, 6
for (auto it = myvector.begin(); it != myvector.end(); ++it)
cout << ' ' << *it;
}
输出:
1 2 3 4 5 6
pop_back() 函数用于从向量后面弹出或删除元素。该值从向量的末尾开始移除,容器大小减 1。
例子:
// CPP program to illustrate
// pop_back() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5 };
myvector.pop_back();
// Vector becomes 1, 2, 3, 4
for (auto it = myvector.begin(); it != myvector.end(); ++it)
cout << ' ' << *it;
}
输出:
1 2 3 4
注意!
“pop_back() 会将值和元素一起删除吗?”
pop_back()
函数会从 vector 的末尾移除一个元素。- 移除元素后,该元素所占用的内存空间会被释放。
- 同时,vector 的长度也会减小 1。
二者区别:
vector::push_back() | vector::pop_back() |
---|---|
它用于在向量末尾添加新元素。 | 它用于删除向量末尾的新元素。 |
它的语法是-:back(value); | 它的语法是-:pop_back(); |
它的参数是我们要添加到向量末尾的值。 | 它不带任何参数。 |
它没有任何返回类型。 | 它没有任何返回值。 |
它的复杂性是恒定的。 | 它的复杂性是恒定的 |
insert()
***std::vector::insert()*是 C++ STL 中的内置函数,用于在指定位置的元素之前插入新元素,从而按插入元素的数量有效增加容器大小。
插入函数被重载以处理多种情况,如下所示:
- 在给定索引处插入一个元素。
- 多次插入一个元素。
- 在给定索引处插入一系列元素。
1. 在给定索引处插入元素
例子:
// C++ program to illustrate the function of
// vector_name.insert(position,val)
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 初始化vetcor容器
vector<int> vector_name{ 1, 2, 3, 4, 5 };
// 打印初始容器
cout << "Original vector :\n";
for (auto x : vector_name)
cout << x << " ";
cout << "\n";
// 在vector的begin()+3位置插入100
vector_name.insert(vector_name.begin() + 3, 100);
// 打印添加后的vector1
cout << "Vector after inserting 100 at position 3 :\n";
for (auto x : vector_name)
cout << x << " ";
cout << "\n";
// 在begin()+1处加入500
vector_name.insert(vector_name.begin() + 1, 500);
// 打印vector
cout << "Vector after inserting 500 at position 1 :\n";
for (auto x : vector_name)
cout << x << " ";
return 0;
}
输出:
Original vector :
1 2 3 4 5
Vector after inserting 100 at position 3 :
1 2 3 100 4 5
Vector after inserting 500 at position 1 :
1 500 2 3 100 4 5
2. 在给定索引处插入多个元素
例子:
// C++ program to illustrate the function of
// vector_name.insert(position,size,val)
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 初始化vector
vector<int> vector_name{ 1, 2, 3, 4, 5 };
//打印最初vector容器
cout << "Original vector :\n";
for (auto x : vector_name)
cout << x << " ";
cout << endl;
//在begin()+3处插入100四次
vector_name.insert(vector_name.begin() + 3, 4, 100);
// 打印更改后的vector容器
cout << "Vector after inserting 100, 4 times, starting "
"at position 3 :\n";
for (auto x : vector_name)
cout << x << " ";
return 0;
}
输出:
Original vector :
1 2 3 4 5
Vector after inserting 100, 4 times, starting at position 3 :
1 2 3 100 100 100 100 4 5
3. 在给定索引处插入元素范围
例子:
// C++ program to illustrate the function of
// vector_name.insert(position,itr1,itr2)
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 初始化vector
vector<int> original{ 1, 2, 3, 4, 5 };
vector<int> temp{ 2, 5, 9, 0, 3, 10 };
// 打印初始vector
cout << "Original vector :\n";
for (auto x : original)
cout << x << " ";
cout << endl;
//这行代码是程序的核心,它利用 vector_name.insert(position, itr1, itr2) 函数将 temp 向量的一部分插入到 original 向量指定的位置。original.insert(...) 调用 original 的 insert 函数进行插入操作。original.begin() + 3 指定插入的位置,即 original 向量下标为 3 的位置之后。temp.begin() + 2 是要插入的元素序列的起始迭代器,指向 temp 向量下标为 2 的元素 (9)。temp.begin() + 5 是要插入的元素序列的结束迭代器,指向 temp 向量下标为 4 的元素 (3) 之后的位置 (不包含该元素)。
original.insert(original.begin() + 3, temp.begin() + 2, temp.begin() + 5);
// 打印修改后的vector
cout << "Vector after Inserting the portion of temp "
"vector in original vector :\n";
for (auto x : original)
cout << x << " ";
return 0;
}
输出:
Original vector :
1 2 3 4 5
Vector after Inserting the portion of temp vector in original vector :
1 2 3 9 0 3 4 5
erase() and clear()+find()
clear ()函数用于删除向量容器的所有元素,从而使其大小为 0。
例子:
// C++ program to demonstrate
// Implementation of clear() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
myvector.push_back(5);
// Vector becomes 1, 2, 3, 4, 5
myvector.clear();
// vector becomes empty
// Printing the vector
for (auto it = myvector.begin(); it != myvector.end();
++it)
cout << ' ' << *it;
return 0;
}
输出:
//无输出
erase()函数用于从容器中删除指定位置或范围的元素。
例子:
1.删除特定位置
// C++ program to demonstrate
// working of erase() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5 };
vector<int>::iterator it;
it = myvector.begin();
myvector.erase(it);
// Printing the Vector
for (auto it = myvector.begin(); it != myvector.end();
++it)
cout << ' ' << *it;
return 0;
}
输出:
2 3 4 5
2.删除范围
// C++ program to demonstrate
// Implementation of erase() function
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5 };
vector<int>::iterator it1, it2;
it1 = myvector.begin();
it2 = myvector.end();
it2--;
it2--;
myvector.erase(it1, it2);
// Printing the Vector
for (auto it = myvector.begin(); it != myvector.end();
++it)
cout << ' ' << *it;
return 0;
}
输出:
4 5
find()函数用于找到特定元素,搭配erase()使用可以实现删除指定元素
例子:
// C++ program to remove element based on its value
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> vector = { 1, 2, 3, 3, 4, 5 };
cout << "vector before deleting " << endl;
for (auto element : vector) {
cout << element << " ";
}
// finding the position of the element in the vector
int valueToBeDeleted = 3;
auto it = find(vector.begin(), vector.end(),
valueToBeDeleted);
if (it != vector.end()) {
vector.erase(it);
}
cout << endl
<< "Vector after deleting valueToBeDeleted "
<< endl;
for (auto element : vector) {
cout << element << " ";
}
cout << endl;
return 0;
}
输出:
vector before deleting
1 2 3 3 4 5
Vector after deleting valueToBeDeleted
1 2 3 4 5
emplace()
通过在该位置插入新元素来扩展容器。仅当需要更多空间时才会进行重新分配。这里容器大小增加一。
例子:
// C++ program to illustrate the
// vector::emplace() function
// insertion at thefront
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int> vec = { 10, 20, 30 };
// insert element by emplace function
// at front
auto it = vec.emplace(vec.begin(), 15);
// print the elements of the vector
cout << "The vector elements are: ";
for (auto it = vec.begin(); it != vec.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
15 10 20 30
emplace_back()
该函数用于向向量容器中插入一个新元素,新元素被添加到向量的末尾。
例子:
// INTEGER VECTOR EXAMPLE
// CPP program to illustrate
// Implementation of emplace() function
#include <iostream>
#include <vector>
using` `namespace` `std;
int` `main()
{
``vector<``int``> myvector;
``myvector.emplace_back(1);
``myvector.emplace_back(2);
``myvector.emplace_back(3);
``myvector.emplace_back(4);
``myvector.emplace_back(5);
``myvector.emplace_back(6);
``// vector becomes 1, 2, 3, 4, 5, 6
``// printing the vector
``for` `(``auto` `it = myvector.begin(); it != myvector.end(); ++it)
``cout << ``' '` `<< *it;
``return` `0;
}
输出:
1 2 3 4 5 6
List容器
基本概念
List是允许非连续内存分配的序列容器*。与vector相比,列表的遍历速度较慢,但一旦找到位置,插入和删除速度很快(恒定时间)。通常,当我们说列表时,我们谈论的是双向doubly linked list(双向链表)。为了实现单链表,我们使用forward_list。*
头文件
#include <list>
C++中声明list的方法
句法:
std::list <data-type> name_of_list;
list的初始化
C++中List的初始化可以通过以下几种方式进行:
1. 直接初始化
std::list<int> list = {1, 2, 3, 4, 5};
这种方式可以直接将元素列表初始化为List。
2. 使用initializer_list
std::list<int> list(initializer_list<int>{1, 2, 3, 4, 5});
这种方式也可以将元素列表初始化为List。
3. 使用构造函数
std::list<int> list(5, 1);
这种方式使用构造函数来初始化List,第一个参数是List的大小,第二个参数是List中每个元素的初始值。
4. 使用迭代器
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> list(vec.begin(), vec.end());
这种方式使用迭代器来初始化List,第一个参数是迭代器指向的第一个元素,第二个参数是迭代器指向的最后一个元素。
5. 使用copy算法
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> list;
std::copy(vec.begin(), vec.end(), std::back_inserter(list));
这种方式使用copy算法来初始化List,第一个参数是源范围的第一个元素的迭代器,第二个参数是源范围的最后一个元素的迭代器,第三个参数是目标范围的第一个元素的迭代器。
示例
#include <iostream>
#include <list>
int main() {
// 直接初始化
std::list<int> list1 = {1, 2, 3, 4, 5};
// 使用 initializer_list
std::list<int> list2(initializer_list<int>{1, 2, 3, 4, 5});
// 使用构造函数
std::list<int> list3(5, 1);
// 使用迭代器
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> list4(vec.begin(), vec.end());
// 使用 copy 算法
std::list<int> list5;
std::copy(vec.begin(), vec.end(), std::back_inserter(list5));
// 打印各个 List
for (int i : list1) {
std::cout << i << " ";
}
std::cout << std::endl;
for (int i : list2) {
std::cout << i << " ";
}
std::cout << std::endl;
for (int i : list3) {
std::cout << i << " ";
}
std::cout << std::endl;
for (int i : list4) {
std::cout << i << " ";
}
std::cout << std::endl;
for (int i : list5) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
输出:
1 2 3 4 5
1 2 3 4 5
1 1 1 1 1
1 2 3 4 5
1 2 3 4 5
List有关的函数
由于有许多与之前vector相同的函数,在这里先总汇,我们专门介绍对于list
相对特殊的函数
功能 | 定义 |
---|---|
front() | 返回列表中第一个元素的值。 |
back() | 返回列表中最后一个元素的值。 |
push_front(g) | 在列表的开头添加一个新元素“g”。 |
push_back(g) | 在列表末尾添加一个新元素“g”。 |
push_front() | 删除列表的第一个元素,并将列表的大小减少 1。 |
pop_back() | 删除列表的最后一个元素,并将列表的大小减少 1。 |
list::begin() | begin() 函数返回一个指向列表第一个元素的迭代器。 |
list::end() | end() 函数返回一个迭代器,该迭代器指向最后一个元素后面的理论最后一个元素。 |
list rbegin() and rend() | rbegin() 返回一个反向迭代器,它指向列表的最后一个元素。rend() 返回一个反向迭代器,该迭代器指向列表开头之前的位置。 |
list cbegin() and cend() | cbegin() 返回一个指向列表开头的常量随机访问迭代器。cend() 返回一个指向列表末尾的常量随机访问迭代器。 |
list crbegin() and crend() | crbegin() 返回一个常量反向迭代器,它指向列表的最后一个元素,即容器的反向开头。crend() 返回一个常量反向迭代器,它指向列表中第一个元素之前的理论元素,即列表的反向末尾。 |
empty | 返回列表是否为空(1)或不为空(0)。 |
insert | 在列表中指定位置的元素之前插入新元素。 |
erase() | 从列表中删除单个元素或一系列元素。 |
assign() | 通过替换当前元素并调整列表大小,将新元素分配给列表。 |
remove | 从列表中删除所有等于给定元素的元素。 |
remove_if() | 用于从列表中删除与作为函数参数给出的谓词或条件相对应的所有值。 |
reverse() | 反转列表。 |
size() | 返回列表中的元素数量。 |
list_resize() | 用于调整列表容器的大小。 |
sort() | 按升序对列表进行排序。 |
list max_size() | 返回列表容器可以容纳的最大元素数。 |
list unique() | 从列表中删除所有重复的连续元素。 |
list::emplace_front() and list::emplace_back() | .emplace_front() 函数用于将新元素插入到列表容器中,并在列表开头就地构造对象。 。emplace_back() 函数用于将新元素插入到列表容器中,并在列表末尾就地构造对象。 |
list::clear() | clear() 函数用于删除列表容器中的所有元素,从而使其大小为 0。 |
运算符= | 该运算符用于通过替换现有内容来将新内容分配给容器。 |
list::swap() | 此函数用于将一个列表的内容与另一个列表交换。 |
list_splice() | 用于将元素从一个列表转移到另一个列表。 |
list_merge() | 将两个已排序的列表合并为一个。 |
list emplace | 通过在给定位置插入新元素来扩展列表,并在列表开头就地构造对象,从而通过避免复制操作来潜在地提高性能 |
下面,我们将介绍之前未介绍的新的方法:
remove_if()
remove() 函数用于从列表中删除与作为函数参数给出的值相对应的所有值
例子:
// CPP program to illustrate
// Implementation of remove() function
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> mylist{ 1, 2, 2, 2, 5, 6, 7 };
mylist.remove(2);
for (auto it = mylist.begin(); it != mylist.end(); ++it)
cout << ' ' << *it;
}
输出:
1 5 6 7
reverse()
reverse()是 C++ STL 中的内置函数,用于反转列表容器。它反转列表容器中元素的顺序。
例子:
// list::reverse() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Creating a list
list<int> demoList;
// Adding elements to the list
demoList.push_back(10);
demoList.push_back(20);
demoList.push_back(30);
demoList.push_back(40);
// Initial list:
cout << "Initial List: ";
for (auto itr = demoList.begin(); itr != demoList.end(); itr++)
cout << *itr << " ";
// reversing the list
demoList.reverse();
// List after reversing the order of elements
cout << "\n\nList after reversing: ";
for (auto itr = demoList.begin(); itr != demoList.end(); itr++)
cout << *itr << " ";
return 0;
}
输出:
Initial List: 10 20 30 40
List after reversing: 40 30 20 10
unique
list::unique()是 C++ STL 中的内置函数,它从列表中删除所有重复的连续元素。它仅适用于排序列表。
例子:
// C++ program to illustrate the
// unique() function
#include <bits/stdc++.h>
using namespace std;
// Function for binary_predicate
bool compare(double a, double b)
{
return ((int)a == (int)b);
}
// Driver code
int main()
{
list<double> list = { 2.55, 3.15, 4.16, 4.16,
4.77, 12.65, 12.65, 13.59 };
cout << "List is: ";
//sort the list
list.sort();
// unique operation on list with no parameters
list.unique();
// starts from the first element
// of the list to the last
for (auto it = list.begin(); it != list.end(); ++it)
cout << *it << " ";
// unique operation on list with parameter
list.unique(compare);
cout << "\nList is: ";
// starts from the first element
// of the list to the last
for (auto it = list.begin(); it != list.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
List is: 2.55 3.15 4.16 4.77 12.65 13.59
List is: 2.55 3.15 4.16 12.65 13.59
splice()
*list **::splice()*是 C++ STL 中的内置函数,用于将元素从一个列表传输到另一个列表。splice() 函数可以通过三种方式使用:
- 将列表x的所有元素转移到另一个列表的某个位置。
- 仅将i指向的元素从列表x传输到列表中的某个位置。
- 将范围[first, last)从列表x转移到另一个列表的某个位置。
**程序1:**传输列表中的所有元素。
例子:
// CPP program to illustrate the
// list::splice() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initializing lists
list<int> l1 = { 1, 2, 3 };
list<int> l2 = { 4, 5 };
list<int> l3 = { 6, 7, 8 };
// transfer all the elements of l2
l1.splice(l1.begin(), l2);
// at the beginning of l1
cout << "list l1 after splice operation" << endl;
for (auto x : l1)
cout << x << " ";
// transfer all the elements of l1
l3.splice(l3.end(), l1);
// at the end of l3
cout << "\nlist l3 after splice operation" << endl;
for (auto x : l3)
cout << x << " ";
return 0;
}
输出:
list l1 after splice operation
4 5 1 2 3
list l3 after splice operation
6 7 8 4 5 1 2 3
**方案2:**传输单个元素。
// CPP program to illustrate the
// list::splice() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initializing lists and iterator
list<int> l1 = { 1, 2, 3 };
list<int> l2 = { 4, 5 };
list<int>::iterator it;
// Iterator pointing to 4
it = l2.begin();
// transfer 4 at the end of l1
l1.splice(l1.end(), l2, it);
cout << "list l1 after splice operation" << endl;
for (auto x : l1)
cout << x << " ";
return 0;
}
输出
list l1 after splice operation
1 2 3 4
**方案 3:**传输一系列元素。
// CPP program to illustrate the
// list::splice() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initializing lists and iterator
list<int> l1 = { 1, 2, 3, 4, 5 };
list<int> l2 = { 6, 7, 8 };
list<int>::iterator it;
// iterator pointing to 1
it = l1.begin();
// advance the iterator by 2 positions
advance(it, 2);
// transfer 3, 4 and 5 at the
// beginning of l2
l2.splice(l2.begin(), l1, it, l1.end());
cout << "list l2 after splice operation" << endl;
for (auto x : l2)
cout << x << " ";
return 0;
}
输出
list l2 after splice operation
3 4 5 6 7 8
merge()
list::merge()是 C++ STL 中的一个内置函数,它将两个排序列表合并为一个。列表应按升序排序。如果没有在参数中传递比较器,则它将两个排序列表合并为一个排序列表。如果在参数中传递比较器,则它会相应地合并列表以进行内部比较。
语法1
例子:
// program below demonstrates the
// merge function in c++
#include <bits/stdc++.h>
using namespace std;
int main()
{
// declaring the lists
// initially sorted
list<int> list1 = { 10, 20, 30 };
list<int> list2 = { 40, 50, 60 };
// merge operation
list2.merge(list1);
cout << "List: ";
for (auto it = list2.begin(); it != list2.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
List:10 20 30 40 50 60
语法2
例子:
// program below demonstrates the
// merge function in c++
#include <bits/stdc++.h>
using namespace std;
// comparator which compares elements internally
bool comparator(int first, int second)
{
return first < second;
}
int main()
{
// declaring the lists
list<int> list1 = { 1, 70, 80 };
list<int> list2 = { 2, 3, 4 };
// merge operation
list1.merge(list2, comparator);
cout << "List: ";
for (auto it = list1.begin(); it != list1.end(); ++it)
cout << *it << " ";
return 0;
}
输出:
List: 1 2 3 4 70 80
set容器
基本概念
集合(Set)是一种关联容器,其中每个元素都必须是唯一的,因为元素的值可以标识它。这些值以特定的排序顺序存储,即升序或降序。
头文件
#include <set>
std ::set类是 C++ 标准模板库 (STL) 的一部分,它在头文件中定义。
C++中set容器的声明
句法:
std::set <数据类型> set_name;
数据类型:Set 可以根据值采用任何数据类型,例如 int、char、float 等。
C++中set的初始化
C++中set的初始化有以下几种方式:
1. 直接使用构造函数
#include <set>
#include <iostream>
int main() {
std::set<int> s = {1, 2, 3, 4, 5};
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
这种方式可以直接初始化一个包含指定元素的set。
2. 使用initializer_list
#include <set>
#include <iostream>
int main() {
std::set<int> s = {1, 2, 3, 4, 5};
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
这种方式与直接使用构造函数类似,但可以使用initializer_list来初始化set。
3. 使用insert()函数
#include <set>
#include <iostream>
int main() {
std::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
这种方式可以逐个插入元素到set中。
4. 使用std::copy()函数
#include <algorithm>
#include <set>
#include <iostream>
int main() {
int numbers[] = {1, 2, 3, 4, 5};
std::set<int> s;
std::copy(numbers, numbers + 5, std::inserter(s, s.begin()));
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
这种方式可以将一个范围内的元素复制到set中。
5. 使用std::initializer_list
#include <initializer_list>
#include <set>
#include <iostream>
int main() {
std::initializer_list<int> il = {1, 2, 3, 4, 5};
std::set<int> s(il);
for (int x : s) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
这种方式可以使用initializer_list来初始化set,并且可以指定set的比较函数。
Set容器的相关函数
功能 | 描述 |
---|---|
begin() | 返回指向集合中第一个元素的迭代器。 |
end() | 返回一个迭代器,指向集合中最后一个元素后面的理论元素。 |
rbegin() | 返回指向容器中最后一个元素的反向迭代器。 |
rend() | 返回一个反向迭代器,该迭代器指向集合容器中第一个元素之前的理论元素。 |
crbegin() | 返回指向容器中最后一个元素的常量迭代器。 |
crend() | 返回一个常量迭代器,指向容器中第一个元素之前的位置。 |
cbegin() | 返回指向容器中第一个元素的常量迭代器。 |
cend() | 返回一个常量迭代器,该迭代器指向容器中最后一个元素之后的位置。 |
size() | 返回集合中元素的数量。 |
max_size() | 返回集合可以容纳的最大元素数。 |
empty() | 返回集合是否为空。 |
insert(const g) | 将新元素“g”添加到集合中。 |
iterator insert(iterator position,const g) | 在迭代器指向的位置添加一个新元素“g”。 |
erase(iterator position) | 删除迭代器指向的位置的元素。 |
erase(const g) | 从集合中删除值“g”。 |
clear() | 从集合中删除所有元素。 |
key_comp()/ value_comp() | 返回确定集合中元素如何排序的对象(默认为“<”)。 |
find(const g) | 如果找到,则返回到集合中元素“g”的迭代器,否则返回到末尾的迭代器。 |
count(const g) | 根据元素“g”是否存在于集合中,返回 1 或 0。 |
lower_bound(const g) | 返回一个迭代器,指向第一个等于“g”的元素,或者绝对不会在集合中的元素“g”之前。 |
upper_bound(const g) | 返回一个迭代器,指向集合中元素“g”之后的第一个元素。 |
equal_range() | 该函数返回一个对的迭代器。(key_comp)。对是指包含容器中所有键等于 k 的元素的范围。 |
安放() | 此函数用于将新元素插入集合容器中,前提是要插入的元素是唯一的并且集合中尚不存在。 |
emplace_hint() | 返回一个迭代器,指向插入完成的位置。如果参数中传递的元素已经存在,则返回一个指向现有元素所在位置的迭代器。 |
swap() | 此函数用于交换两个集合的内容,但这些集合必须具有相同类型,尽管大小可能不同。 |
运算符= | “=”是 C++ STL 中的一个运算符,用于将一个集合复制(或移动)到另一个集合,而 set::operator= 是相应的运算符函数。 |
get_allocator() | 返回与该集合关联的分配器对象的副本。 |
下面介绍set中较重要的几个成员函数:
key_comp()/value_camp()
set::key_comp()/set ::value_comp()是 C++ STL 中的内置函数,它返回容器使用的比较对象的副本。**默认情况下,这是一个 less 对象,它返回与运算符’<'**相同的结果。该对象决定容器中元素的顺序。它是一个函数指针或函数对象,它采用与容器元素类型相同的两个参数,如果第一个参数被认为在其定义的严格弱排序中位于第二个参数之前,则返回true ,否则返回 false。*如果 key_comp 自反地返回false(即,无论元素作为参数传递的顺序如何),则 认为集合中的两个元素是等效的。
例子:
// C++ program to illustrate the
// set::key_comp() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// creating a set named 'a'
set<int> a;
set<int>::key_compare comp = a.key_comp()//a.value_comp;
// Inserting elements into set
for (int i = 0; i <= 10; i++)
a.insert(i);
cout << "Set a has the numbers: ";
// stores the last value of the set
int l = *a.rbegin();
// initialising the iterator
set<int>::iterator it = a.begin();
// printing elements of all set
do {
cout << *it << " ";
} while (comp(*(++it), l));
return 0;
}
输出:
Set a has the numbers: 0 1 2 3 4 5 6 7 8 9
lower_bound()
它返回一个指向容器中元素的迭代器,相当于参数中传递的k。如果集合容器中不存在 k,则该函数返回一个迭代器,该迭代器指向紧邻的下一个元素(该元素仅大于 k)。如果参数中传入的key超过了容器中的最大值,则迭代器返回的指向集合容器中最后一个元素之外的元素。
例子:
// CPP program to demonstrate the
// set::lower_bound() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
// Function to insert elements
// in the set container
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(6);
cout << "The set elements are: ";
for (auto it = s.begin(); it != s.end(); it++)
cout << *it << " ";
// when 2 is present
auto it = s.lower_bound(2);
if (it != s.end()) {
cout << "\nThe lower bound of key 2 is ";
cout << (*it) << endl;
}
else
cout << "The element entered is larger than the "
"greatest element in the set"
<< endl;
// when 3 is not present
// points to next greater after 3
it = s.lower_bound(3);
if (it != s.end()) {
cout << "The lower bound of key 3 is ";
cout << (*it) << endl;
}
else
cout << "The element entered is larger than the "
"greatest element in the set"
<< endl;
// when 8 exceeds the max element in set
it = s.lower_bound(8);
if (it != s.end()) {
cout << "The lower bound of key 8 is ";
cout << (*it) << endl;
}
else
cout << "The element is larger than the greatest "
"element in the set"
<< endl;
return 0;
}
输出:
The set elements are: 1 2 4 5 6
The lower bound of key 2 is 2
The lower bound of key 3 is 4
The element is larger than the greatest element in the set
upper_bound
它返回一个指向下一个仅大于 k 的元素的迭代器。如果参数传入的键超过容器中的最大键,则迭代器返回指向集合容器中 最后一个元素(可以使用set::end()函数识别)的下一个元素。
句法:
set_name.upper_bound(key)
**参数:**此函数接受单个强制参数键,该键指定要返回其上限的元素。
**返回值:**该函数返回一个迭代器,指向下一个大于 k 的元素。如果参数中传递的键超过了容器中的最大键,则迭代器指向 std::end(),它指向集合中最后一个元素的下一个元素。
例子:
// CPP program to demonstrate the
// set::upper_bound() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
// Function to insert elements
// in the set container
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(6);
cout << "The set elements are: ";
for (auto it = s.begin(); it != s.end(); it++)
cout << *it << " ";
// when 2 is present
// points to next element after 2
auto it = s.upper_bound(2);
cout << "\nThe upper bound of key 2 is ";
cout << (*it) << endl;
// when 3 is not present
// points to next greater after 3
it = s.upper_bound(3);
cout << "The upper bound of key 3 is ";
cout << (*it) << endl;
return 0;
}
输出:
The set elements are: 1 2 4 5 6
The upper bound of key 2 is 4
The upper bound of key 3 is 4
equal_range()
它返回对的迭代器。对是指包含容器中所有键等于 k 的元素的范围。由于 set 包含唯一元素,因此下界将是元素本身,上限将指向键 k 之后的下一个元素。如果没有与键 K 匹配的元素,则返回的范围长度为 0,两个迭代器都指向根据容器内部比较对象 (key_comp) 大于 k 的第一个元素。如果键超过集合容器中的最大元素,则返回一个指向集合容器中最后一个元素的迭代器。
句法:
set_name.equal_range(key)
**参数:**该函数接受一个强制参数key,该参数指定要返回 set 容器中范围的 key。 **返回值:**该函数返回一个对的迭代器。(key_comp)。对是指包含容器中所有键等于 k 的元素的范围。由于 set 包含唯一元素,因此下界将是元素本身,上限将指向键 k 之后的下一个元素。如果没有与键 K 匹配的元素,则返回的范围长度为 0,两个迭代器都指向根据容器内部比较对象 (key_comp) 大于 k 的第一个元素。如果键超过集合容器中的最大元素,则返回一个指向集合容器中最后一个元素的迭代器。下面的程序说明了上述功能。
例子:
// CPP program to demonstrate the
// set::equal_range() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(3);
// prints the set elements
cout << "The set elements are: ";
for (auto it = s.begin(); it != s.end(); it++)
cout << *it << " ";
// Function returns lower bound and upper bound
auto it = s.equal_range(2);
cout << "\nThe lower bound of 2 is " << *it.first;
cout << "\nThe upper bound of 2 is " << *it.second;
// Function returns the last element
it = s.equal_range(8);
cout << "\nThe lower bound of 8 is " << *it.first;
cout << "\nThe upper bound of 8 is " << *it.second;
// Function returns the range where the
// element greater than 0 lies
it = s.equal_range(0);
cout << "\nThe lower bound of 0 is " << *it.first;
cout << "\nThe upper bound of 0 is " << *it.second;
return 0;
}
输出:
The set elements are: 1 2 3 4 5
The lower bound of 2 is 2
The upper bound of 2 is 3
The lower bound of 8 is 5
The upper bound of 8 is 5
The lower bound of 0 is 1
The upper bound of 0 is 1
emplace_hint()
用于在集合中插入新元素。函数的参数中传递一个位置,该位置充当在当前位置插入元素之前搜索操作从何处开始的提示。该位置只会帮助进程变得更快,它并不能决定新元素插入的位置。新元素仅插入到设置容器的属性之后。
句法:
set_name.emplace_hint(迭代器位置,值)
**参数:**该函数接受两个强制参数,如下所述:
- **位置:**此参数充当在当前位置插入元素之前执行搜索操作的提示。该位置只会帮助过程更快,它并不能决定新元素插入的位置。新元素仅插入到设置容器的属性之后。
- **value:**指定要插入到集合容器中的元素。如果该值之前不存在,则将其插入到集合中。
**返回值:**该函数返回一个指向插入完成位置的迭代器。如果参数中传递的元素已经存在,则返回一个指向现有元素所在位置的迭代器。下面的程序说明了上述功能。
例子:
// CPP program to demonstrate the
// set::emplace_hint() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
set<int> s;
auto it = s.emplace_hint(s.begin(), 1);
// stores the position of 2's insertion
it = s.emplace_hint(it, 2);
// fast step as it directly
// starts the search step from
// position where 3 was last inserted
s.emplace_hint(it, 3);
// this is a slower step as
// it starts checking from the
// position where 3 was inserted
// but 0 is to be inserted before 1
s.emplace_hint(it, 0);
// prints the set elements
for (auto it = s.begin(); it != s.end(); it++)
cout << *it << " ";
return 0;
}
输出:
0 1 2 3
Map容器
基本概念
Map是一种关联容器:实现可快速搜索的排序数据结构。Map是以映射方式存储元素的关联容器每个元素都有一个键值和一个映射值。两个映射值不能具有相同的键值。
头文件
#include <map>
C++中Map的声明
#include <map>
// 声明一个key为int,value为string的map
std::map<int, std::string> myMap;
C++中Map的初始化
C++中Map的初始化有以下几种方式:
1. 直接使用构造函数
#include <map>
#include <iostream>
int main() {
std::map<int, std::string> m = {{1, "one"}, {2, "two"}, {3, "three"}};
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
这种方式可以直接初始化一个包含指定键值对的map。
2. 使用initializer_list
#include <initializer_list>
#include <map>
#include <iostream>
int main() {
std::initializer_list<std::pair<int, std::string>> il = {{1, "one"}, {2, "two"}, {3, "three"}};
std::map<int, std::string> m(il);
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
这种方式与直接使用构造函数类似,但可以使用initializer_list来初始化map。
3. 使用insert()函数
#include <map>
#include <iostream>
int main() {
std::map<int, std::string> m;
m.insert({1, "one"});
m.insert({2, "two"});
m.insert({3, "three"});
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
这种方式可以逐个插入键值对到map中。
4. 使用std::copy()函数
#include <algorithm>
#include <map>
#include <iostream>
int main() {
std::pair<int, std::string> numbers[] = {{1, "one"}, {2, "two"}, {3, "three"}};
std::map<int, std::string> m;
std::copy(numbers, numbers + 3, std::inserter(m, m.begin()));
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
这种方式可以将一个范围内的键值对复制到map中。
5. 使用std::initializer_list
#include <initializer_list>
#include <map>
#include <iostream>
int main() {
std::initializer_list<std::pair<int, std::string>> il = {{1, "one"}, {2, "two"}, {3, "three"}};
std::map<int, std::string> m(il, [](const std::pair<int, std::string>& p) { return p.first; });
for (auto it = m.begin(); it != m.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
这种方式可以使用initializer_list来初始化map,并且可以指定map的比较函数。
Map的相关函数
方法 | 定义 |
---|---|
map::insert() | 在映射容器中插入具有特定键的元素 –> O(log n) |
map:: count() | 返回映射中键值“g”的元素的匹配数。–> O(log n) |
map equal_range() | 返回对的迭代器。该对指的是一个范围的边界,该范围包括容器中具有等于 k 的键的所有元素。 |
map erase() | 用于从容器中删除元素 –> O(log n) |
map rend() | 返回一个反向迭代器,指向映射中第一个键值对之前的理论元素(被视为其反向末端)。 |
map rbegin() | 返回一个反向迭代器,它指向映射的最后一个元素。 |
map find() | 如果找到,则返回映射中键值“g”的元素的迭代器,否则返回迭代器结束。 |
map crbegin() and crend() | crbegin() 返回一个常量反向迭代器,引用映射容器中的最后一个元素。crend() 返回一个常量反向迭代器,指向映射中第一个元素之前的理论元素。 |
map cbegin() and cend() | cbegin() 返回一个常量迭代器,引用映射容器中的第一个元素。cend() 返回一个常量迭代器,指向多重映射中最后一个元素后面的理论元素。 |
下面介绍几个map中常用的函数:
count()
C++ STL 中的内置函数,如果映射容器中存在带有键 K 的元素,则返回 1。如果容器中不存在具有键 K 的元素,则返回 0。
句法:
map_name.count(键 k)
**参数:**该函数接受强制参数k,它指定要在映射容器中搜索的键。 **返回值:**该函数返回键 K 在地图容器中出现的次数。如果该键存在于容器中,则返回 1,因为映射仅包含唯一键。如果映射容器中不存在该键,则返回 0。下面的程序说明了上述功能:
// C++ program to illustrate
// the map::count() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialize container
map<int, int> mp;
// insert elements in random order
mp.insert({ 2, 30 });
mp.insert({ 1, 40 });
mp.insert({ 3, 60 });
mp.insert({ 4, 20 });
mp.insert({ 5, 50 });
// checks if key 1 is present or not
if (mp.count(1))
cout << "The key 1 is present\n";
else
cout << "The key 1 is not present\n";
// checks if key 100 is present or not
if (mp.count(100))
cout << "The key 100 is present\n";
else
cout << "The key 100 is not present\n";
return 0;
}
输出:
The key 1 is present
The key 100 is not present
insert()
***insert()*是 C++ STL 中的内置函数,用于在映射容器中插入具有特定键的元素。
- 句法:
迭代器map_name.insert({key, element})
**参数:**该函数接受由键和要插入到地图容器中的元素组成的对。如果键已存在于映射中,则该函数不会将键和元素插入到映射中。
**返回值:**该函数返回一个对,其成员pair::first设置为一个迭代器,该迭代器指向新插入的元素或指向映射中具有等效键的元素。如果插入了新元素,则对中的pair::第二个元素设置为 true;如果等效键已存在,则设置为 false。
例子:
// C++ program to illustrate
// map::insert({key, element})
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialize container
map<int, int> mp;
// insert elements in random order
mp.insert({ 2, 30 });
mp.insert({ 1, 40 });
mp.insert({ 3, 60 });
// does not inserts key 2 with element 20
mp.insert({ 2, 20 });
mp.insert({ 5, 50 });
// prints the elements
cout << "KEY\tELEMENT\n";
for (auto itr = mp.begin(); itr != mp.end(); ++itr) {
cout << itr->first
<< '\t' << itr->second << '\n';
}
return 0;
}
输出:
关键元素
1 40
2 30
3 60
5 50
equal_range()
它返回一对迭代器。该对指的是一个范围的边界,该范围包括容器中具有等于 k 的键的所有元素。由于映射容器仅包含唯一键,因此返回的对中的第一个迭代器因此指向该元素,对中的第二个迭代器指向键 K 之后的下一个键。如果没有与键 K 匹配,则key K 大于最大 key ,返回的范围长度为 1,两个迭代器都指向一个元素,该元素的 key 表示映射和元素的大小为 0。否则下界和上界都指向该元素大于键 K。
例子:
// C++ program to illustrate the
// map::equal_range() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialize container
map<int, int> mp;
// insert elements in random order
mp.insert({ 4, 30 });
mp.insert({ 1, 40 });
mp.insert({ 6, 60 });
pair<map<int, int>::iterator,
map<int, int>::iterator>
it;
// iterator of pairs
it = mp.equal_range(1);
cout << "The lower bound is "
<< it.first->first
<< ":" << it.first->second;
cout << "\nThe upper bound is "
<< it.second->first
<< ":" << it.second->second;
return 0;
}
输出:
The lower bound is1:40
The upper bound is4:30
erase()
- *erase的语法:*
map.erase(key)
***参数:*该函数接受一个强制参数键,该参数指定要在地图容器中删除的键。
****返回值:****如果在映射中找到关键元素,则该函数返回 1,否则返回 0。
例子:
// C++ program to illustrate
// map::erase(key)
#include <bits/stdc++.h>
using namespace std;
int main()
{
// initialize container
map<int, int> mp;
// insert elements in random order
mp.insert({ 2, 30 });
mp.insert({ 1, 40 });
mp.insert({ 3, 60 });
mp.insert({ 5, 50 });
// prints the elements
cout << "The map before using erase() is : \n";
cout << "KEY\tELEMENT\n";
for (auto itr = mp.begin(); itr != mp.end(); ++itr) {
cout << itr->first
<< '\t' << itr->second << '\n';
}
// function to erase given keys
mp.erase(1);
mp.erase(2);
// prints the elements
cout << "\nThe map after applying erase() is : \n";
cout << "KEY\tELEMENT\n";
for (auto itr = mp.begin(); itr != mp.end(); ++itr) {
cout << itr->first
<< '\t' << itr->second << '\n';
}
return 0;
}
输出
The map before using erase() is :
KEY ELEMENT
1 40
2 30
3 60
5 50
The map after applying erase() is :
KEY ELEMENT
3 60
5 50
find()
它返回一个迭代器或常量迭代器,该迭代器引用键在映射中出现的位置。如果映射容器中不存在该键,则返回一个迭代器或引用map.end()的常量迭代器
例子:
// C++ program for illustration
// of map::find() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Initialize container
map<int, int> m;
// Insert elements in random order
m.insert({ 2, 30 });
m.insert({ 1, 40 });
m.insert({ 3, 20 });
m.insert({ 4, 50 });
int s1=2; //element1 to find (exist in the map)
int s2=5; //element2 to find (does not exist in the map)
cout << "Element "<<s1;
if(m.find(s1)!=m.end()){
//if the element is found before the end of the map
cout<<" : found : Value : "<<m[s1]<<endl;
//if the element is present then you can access it using the index
}
else cout<<" : Not found"<<endl;
cout << "Element "<<s2;
if(m.find(s2)!=m.end()){
//if the element is found before the end of the map
cout<<" : found : Value : "<<m[s2];
//if the element is present then you can access it using the index
}
else cout<<" : Not found"<<endl;
return 0;
}
输出
Element 2 : found : Value : 30
Element 5 : Not found
Stack容器
基本概念
堆栈是一种采用 LIFO(后进先出)工作方式的容器适配器(为顺序容器提供不同的接口),其中在一端(顶部)添加新元素,并仅从该端删除元素。Stack 使用向量或双端队列默认情况下)或列表(顺序容器类)的封装对象作为其底层容器,提供一组特定的成员函数来访问其元素。
P.S
如果在记住**堆栈(stack)和队列(queue)**之间的基本区别时感到困惑,那么只需举一个现实生活中的例子来说明这种区别,对于堆栈stack,书的堆叠我们可以轻松地拿走最上面的书,而对于队列,请记住当你必须站在队列前面时ATM取现金,那么第一个靠近ATM的人就有第一个机会从ATM取钱。因此,队列是 FIFO(先进先出)类型的工作方式。
头文件
#include <stack>
C++中stack的声明方法
为了创建堆栈,我们必须在代码中包含 头文件。然后我们使用此语法来定义 std::stack:
template <class Type, class Container = deque<Type> > 类栈;
Type – 是 std::stack 中包含的元素的类型。它可以是任何有效的 C++ 类型,甚至可以是用户定义的类型。
容器– 是底层容器对象的类型。
参数类型:-
value_type- 第一个模板参数 T。它表示元素类型。
container_type-第二个模板参数,Container。它表示底层容器类型。
size_type - 无符号整型。
C++中stack的初始化
C++中stack的初始化有以下几种方式:
1. 直接使用构造函数
#include <stack>
#include <iostream>
int main() {
std::stack<int> s;
// 使用push()函数压入元素
s.push(1);
s.push(2);
s.push(3);
// 使用top()函数获取栈顶元素
std::cout << s.top() << std::endl;
// 使用pop()函数弹出元素
s.pop();
// 使用size()函数获取栈的大小
std::cout << s.size() << std::endl;
return 0;
}
这种方式可以直接创建一个空的stack。
2. 使用initializer_list
#include <initializer_list>
#include <stack>
#include <iostream>
int main() {
std::initializer_list<int> il = {1, 2, 3};
std::stack<int> s(il);
// 使用top()函数获取栈顶元素
std::cout << s.top() << std::endl;
// 使用pop()函数弹出元素
s.pop();
// 使用size()函数获取栈的大小
std::cout << s.size() << std::endl;
return 0;
}
这种方式可以使用initializer_list来初始化stack。
3. 使用std::copy()函数
#include <algorithm>
#include <stack>
#include <iostream>
int main() {
int numbers[] = {1, 2, 3};
std::stack<int> s;
std::copy(numbers, numbers + 3, std::back_inserter(s));
// 使用top()函数获取栈顶元素
std::cout << s.top() << std::endl;
// 使用pop()函数弹出元素
s.pop();
// 使用size()函数获取栈的大小
std::cout << s.size() << std::endl;
return 0;
}
这种方式可以将一个范围内的元素复制到stack中。
4. 使用std::initializer_list
#include <initializer_list>
#include <stack>
#include <iostream>
int main() {
std::initializer_list<int> il = {1, 2, 3};
std::stack<int> s(il, [](const int& x) { return -x; });
// 使用top()函数获取栈顶元素
std::cout << s.top() << std::endl;
// 使用pop()函数弹出元素
s.pop();
// 使用size()函数获取栈的大小
std::cout << s.size() << std::endl;
return 0;
}
这种方式可以使用initializer_list来初始化stack,并且可以指定stack的比较函数。
Stack的相关函数
empty()和size()
empty() 函数用于检查堆栈容器是否为空。
例子:
// CPP program to illustrate
// Implementation of empty() function
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> mystack;
mystack.push(1);
// Stack becomes 1
if (mystack.empty()) {
cout << "True";
}
else {
cout << "False";
}
return 0;
}
输出:
False
***size()*函数用于返回堆栈容器的大小或堆栈容器中元素的数量
例子
// CPP program to illustrate
// Implementation of size() function
#include <iostream>
#include <stack>
using namespace std;
int main()
{
int sum = 0;
stack<int> mystack;
mystack.push(1);
mystack.push(8);
mystack.push(3);
mystack.push(6);
mystack.push(2);
// Stack becomes 1, 8, 3, 6, 2
cout << mystack.size();
return 0;
}
输出:
5
二者的区别
堆栈empty() | 堆栈size() | |
---|---|---|
1. | 用于返回栈是否为空 | 它用于返回堆栈中元素的数量。 |
2. | 它的语法是-:empty() | 它的语法是-:size() |
3. | 它的返回类型是布尔值。 | 它的返回类型是整数。 |
4. | 它不带任何参数。 | 它不带任何参数。 |
5. | 它的复杂性是恒定的。 | 它的复杂性是恒定的。 |
top()
top() 函数用于引用堆栈的顶部(或最新)元素。
例子:
// CPP program to illustrate
// Implementation of top() function
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> mystack;
mystack.push(5);
mystack.push(1);
mystack.push(2);
// Stack top
cout << mystack.top();
return 0;
}
输出:
2
push()和pop()
push() 函数用于在堆栈顶部插入或“推送”元素。该函数属于**头文件。该元素被添加到栈容器中,栈的大小加1。
例子:
// CPP program to illustrate
// Implementation of push() function
#include <iostream>
#include <stack>
using namespace std;
int main()
{
// Empty stack
stack<int> mystack;
mystack.push(0);
mystack.push(1);
mystack.push(2);
// Printing content of stack
while (!mystack.empty()) {
cout << ' ' << mystack.top();
mystack.pop();
}
}
输出:
2 1 0
***pop ()**函数用于从堆栈顶部删除或“弹出”元素(堆栈中最新或最顶部的元素)。该函数属于*头文件。该元素从栈容器中移除,栈的大小减 1。
例子:
// CPP program to illustrate
// Implementation of pop() function
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> mystack;
mystack.push(1);
mystack.push(2);
mystack.push(3);
mystack.push(4);
// Stack becomes 1, 2, 3, 4
mystack.pop();
mystack.pop();
// Stack becomes 1, 2
while (!mystack.empty()) {
cout << ' ' << mystack.top();
mystack.pop();
}
}
输出:
2 1
Queue容器
基本介绍
队列是一种容器适配器(为顺序容器提供不同的接口),以先进先出 (FIFO) 类型的排列方式运行。元素从后面(末尾)插入,从前面删除。队列使用或list(顺序容器类)的封装对象作为其底层容器,提供一组特定的成员函数来访问其元素。
头文件
#include <queue>
C++中的queue的声明方式
C++中queue的相关函数
方法 | 定义 |
---|---|
queue::empty() | 返回队列是否为空。如果队列为空则返回 true,否则返回 false。 |
queue::size() | 返回队列的大小。 |
queue::swap() | 交换两个队列的内容,但队列必须具有相同的数据类型,尽管大小可能不同。 |
queue::emplace() | 向队列容器中插入一个新元素,新元素被添加到队列末尾。 |
queue::front() | 返回对队列第一个元素的引用。 |
queue::back() | 返回对队列最后一个元素的引用。 |
queue::push(g) | 将元素“g”添加到队列末尾。 |
queue::pop() | 删除队列的第一个元素。 |
swap()
wap() 函数用于交换两个队列的内容,但队列必须具有相同的类型,尽管大小可能不同。
例子:
// CPP program to illustrate
// Implementation of swap() function
#include <bits/stdc++.h>
using namespace std;
int main()
{
// Take any two queues
queue<char> queue1, queue2;
int v = 96;
for (int i = 0; i < 5; i++) {
queue1.push(v + 1);
v++;
}
for (int i = 0; i < 4; i++) {
queue2.push(v + 1);
v++;
}
// Swap elements of queues
queue1.swap(queue2);
// Print the first queue
cout << "queue1 = ";
while (!queue1.empty()) {
cout << queue1.front() << " ";
queue1.pop();
}
// Print the second set
cout << endl << "queue2 = ";
while (!queue2.empty()) {
cout << queue2.front() << " ";
queue2.pop();
}
return 0;
}
输出:
queue1 = f g h i
queue2 = a b c d e
emplace()
该函数用于向队列容器中插入一个新元素,新元素被添加到队列末尾。
例子:
// INTEGER queue EXAMPLE
// CPP program to illustrate
// Implementation of emplace() function
#include <iostream>
#include <queue>
using namespace std;
int main()
{
queue<int> myqueue;
myqueue.emplace(1);
myqueue.emplace(2);
myqueue.emplace(3);
myqueue.emplace(4);
myqueue.emplace(5);
myqueue.emplace(6);
// queue becomes 1, 2, 3, 4, 5, 6
while (!myqueue.empty())
{
cout << ' ' << myqueue.front();
myqueue.pop();
}
return 0;
}
输出:
1 2 3 4 5 6
push()和pop()
*队列容器的**push ()*函数用于将元素插入到队列的末尾。该函数属于*头文件。该元素被添加到队列容器中,并且队列的大小增加 1。
句法
队列名称 push(值)
- 要插入的元素的值作为参数传递。
结果
- 添加一个与队列末尾传递的参数值相同的元素。
例子:
// CPP program to illustrate
// Application of push() and pop() function
#include <iostream>
#include <queue>
using namespace std;
int main()
{
// Empty Queue
int c = 0;
queue<int> myqueue;
myqueue.push(5);
myqueue.push(13);
myqueue.push(0);
myqueue.push(9);
myqueue.push(4);
// queue becomes 5, 13, 0, 9, 4
// Counting number of elements in queue
while (!myqueue.empty()) {
myqueue.pop();
c++;
}
cout << c;
}
输出
0 1 2
pop()
队列容器的pop()函数用于从队列的前面删除一个元素(队列中最旧的元素)。该元素从队列容器中删除,队列大小减 1。
句法
队列名称 pop()
参数
- 没有传递参数
结果
- 删除队列中最旧的元素或基本上是最前面的元素。
例子:
// CPP program to illustrate
// Application of push() and pop() function
#include <iostream>
#include <queue>
using namespace std;
int main()
{
// Empty Queue
int c = 0;
queue<int> myqueue;
myqueue.push(5);
myqueue.push(13);
myqueue.push(0);
myqueue.push(9);
myqueue.push(4);
// queue becomes 5, 13, 0, 9, 4
// Counting number of elements in queue
while (!myqueue.empty()) {
myqueue.pop();
c++;
}
cout << c;
}
输出:
5
C++中队列push()和队列pop()的区别
下表列出了queue::push()和queue::pop()之间的区别:
S. 编号 | 队列推送() | 队列弹出() |
---|---|---|
1. | 它用于在队列末尾插入一个新元素。 | 它用于删除队列中的下一个元素 |
2. | 它的语法是-: push(value_type&&val); | 它的语法是-: pop(); |
3. | 它需要一个参数,即要插入的值。 | 它不带任何参数。 |
4. | 它的返回类型是void。 | 它的返回类型是void。 |
5. | 它存在于****头文件中。 | 它存在于****头文件中。 |
迭代器
最后,让大家来学习迭代器,因为它的概念并不困难,并且我猜你以及能对它进行大概的描述。顾名思义,迭代器用于处理一系列值。它们是 STL 通用性的主要特征。
迭代器的操作
begin()**:- 该函数用于返回容器的 **开始位置。
end():- 该函数用于返回容器的结束位置。
// C++ code to demonstrate the working of
// iterator, begin() and end()
#include<iostream>
#include<iterator> // for iterators
#include<vector> // for vectors
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr;
// Displaying vector elements using begin() and end()
cout << "The vector elements are : ";
for (ptr = ar.begin(); ptr < ar.end(); ptr++)
cout << *ptr << " ";
return 0;
}
输出:
The vector elements are : 1 2 3 4 5
advance():
该函数用于增加迭代器位置,直到达到其参数中提到的指定数字。
// C++ code to demonstrate the working of
// advance()
#include<iostream>
#include<iterator> // for iterators
#include<vector> // for vectors
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr = ar.begin();
// Using advance() to increment iterator position
// points to 4
advance(ptr, 3);
// Displaying iterator position
cout << "The position of iterator after advancing is : ";
cout << *ptr << " ";
return 0;
}
输出:
The position of iterator after advancing is : 4
next():
该函数返回迭代器在前进其参数中提到的位置后将指向的新迭代器。
prev():
该函数返回迭代器在递减其参数中提到的位置后将指向的新迭代器。
// C++ code to demonstrate the working of
// next() and prev()
#include<iostream>
#include<iterator> // for iterators
#include<vector> // for vectors
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterators to a vector
vector<int>::iterator ptr = ar.begin();
vector<int>::iterator ftr = ar.end();
// Using next() to return new iterator
// points to 4
auto it = next(ptr, 3);
// Using prev() to return new iterator
// points to 3
auto it1 = prev(ftr, 3);
// Displaying iterator position
cout << "The position of new iterator using next() is : ";
cout << *it << " ";
cout << endl;
// Displaying iterator position
cout << "The position of new iterator using prev() is : ";
cout << *it1 << " ";
cout << endl;
return 0;
}
输出:
The position of new iterator using next() is : 4
The position of new iterator using prev() is : 3
inserter():
- 该函数用于将元素插入到容器中的任意位置。它接受2 个参数,即容器和迭代器(用于插入元素的位置)。
// C++ code to demonstrate the working of
// inserter()
#include<iostream>
#include<iterator> // for iterators
#include<vector> // for vectors
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
vector<int> ar1 = {10, 20, 30};
// Declaring iterator to a vector
vector<int>::iterator ptr = ar.begin();
// Using advance to set position
advance(ptr, 3);
// copying 1 vector elements in other using inserter()
// inserts ar1 after 3rd position in ar
copy(ar1.begin(), ar1.end(), inserter(ar,ptr));
// Displaying new vector elements
cout << "The new vector after inserting elements is : ";
for (int &x : ar)
cout << x << " ";
return 0;
}
输出:
The new vector after inserting elements is : 1 2 3 10 20 30 4 5
迭代器的类型:
在浏览了各种 STL 算法(如std::find、std::equal、std::count )的模板定义之后,你一定已经找到了由Input Iterator类型的对象组成的模板定义。那么它们是什么以及为什么使用它们?
迭代器层次结构
六种迭代器的关系
六种迭代器之间存在以下关系:
- 输入迭代器 是 输出迭代器 的逆。
- 前向迭代器 是 后向迭代器 的逆。
- 双向迭代器 是 前向迭代器 和 后向迭代器 的结合。
- 随机迭代器 是 双向迭代器 的扩展。
输入迭代器
定义
输入迭代器是一种只能用于读取元素的迭代器。它具有以下特点:
- 可以使用
++
运算符递增迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 不能使用
--
运算符递减迭代器。 - 不能使用
[]
运算符访问迭代器指向的元素。
用法
输入迭代器通常用于从输入流中读取数据。例如,可以使用 std::cin
的迭代器来读取标准输入。
std::vector<int> vec;
for (std::cin >> i; i != EOF; ++i) {
vec.push_back(i);
}
输出迭代器
定义
输出迭代器是一种只能用于写入元素的迭代器。它具有以下特点:
- 可以使用
++
运算符递增迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 不能使用
--
运算符递减迭代器。 - 可以使用
[]
运算符访问迭代器指向的元素。
用法
输出迭代器通常用于将数据写入输出流。例如,可以使用 std::cout
的迭代器来将数据写入标准输出。
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
前向迭代器
定义
前向迭代器是一种只能从头到尾遍历集合的迭代器。它具有以下特点:
- 可以使用
++
运算符递增迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 不能使用
--
运算符递减迭代器。 - 不能使用
[]
运算符访问迭代器指向的元素。
用法
前向迭代器通常用于遍历容器中的元素。例如,可以使用 std::vector
的迭代器来遍历向量中的元素。
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it *= 2;
}
后向迭代器
定义
后向迭代器是一种只能从尾到头遍历集合的迭代器。它具有以下特点:
- 可以使用
--
运算符递减迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 不能使用
++
运算符递增迭代器。 - 不能使用
[]
运算符访问迭代器指向的元素。
用法
后向迭代器通常用于从后向前遍历容器中的元素。例如,可以使用 std::list
的迭代器来从后向前遍历链表中的元素。
std::list<int> lst = {1, 2, 3, 4, 5};
for (auto it = lst.rbegin(); it != lst.rend(); ++it) {
*it *= 2;
}
双向迭代器
定义
双向迭代器是一种可以从头到尾或从尾到头遍历集合的迭代器。它具有以下特点:
- 可以使用
++
运算符递增迭代器。 - 可以使用
--
运算符递减迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 不能使用
[]
运算符访问迭代器指向的元素。
用法
双向迭代器可以用于从头到尾或从尾到头遍历容器中的元素。例如,可以使用 std::deque
的迭代器来从头到尾或从尾到头遍历双端队列中的元素。
std::deque<int> deq = {1, 2, 3, 4, 5};
for (auto it = deq.begin(); it != deq.end(); ++it) {
*it *= 2;
}
for (auto it = deq.rbegin(); it != deq.rend(); ++it) {
*it *= 2;
}
随机迭代器
定义
随机迭代器是一种可以直接访问集合中的任何元素的迭代器。它具有以下特点:
- 可以使用
++
运算符递增迭代器。 - 可以使用
--
运算符递减迭代器。 - 可以使用
*
运算符获取迭代器指向的元素。 - 可以使用
[]
运算符访问迭代器指向的元素。
用法
随机迭代器可以用于直接访问集合中的任何元素。例如,可以使用 std::array
的迭代器来直接访问数组中的任何元素。
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << arr[2] << std::endl; // 输出 3
// 使用随机迭代器访问数组元素
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
打个总结~
如果你能看到这里,我希望你通过阅读会学到新东西,这也是我分享的原因之一,在C++学习曲线中,STL是对新手不友好的,但我希望你和我一样能花时间并有勇气学好,也欢迎大家的指正和交流~共勉!o( ̄▽ ̄)ブ
参考文献
https://www.geeksforgeeks.org/the-c-standard-template-library-stl/ geeksforgeeks网站
《C++ Permier Plus》[美]Stephen Prata 人民邮电出版社,中国公信出版集团出版
https://www.bilibili.com/video/BV1et411b73Z?p=197&vd_source=707c30474de48bfbd6aa4c079c3e5b9f
《黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难》
编写工具
Typora/Visual Studio/Gemini/Xmind