C++青少年简明教程:STL入门介绍
STL(Standard Template Library),这是C++标准库(Standard Library)之一,提供了丰富的模板类和函数,包括容器(如vector、deque、list等)、算法(如排序、搜索等)和迭代器等。学生可以学习如何使用STL来简化程序开发,并提高代码的可重用性。
STL已经成为C++开发的重要基础,广泛应用于包括编程竞赛、游戏开发、高性能计算等众多领域。
C++的STL(Standard Template Library)包含了很多内容,对于初学者来说,入门STL并不需要一次性掌握所有内容,而是应该分阶段、有重点地学习。
常用的容器( Containers )
序列容器(Sequence Containers):管理数据集合的顺序结构。典型的序列容器包括vector(动态数组)、deque(双端队列)、list(双向链表)。
关联容器(Associative Containers):支持快速元素查找和访问。主要的关联容器有set、map。
无序关联容器(Unordered Associative Containers):基于哈希表实现,可以提供平均时间复杂度为常数的元素查找。包括unordered_set、unordered_map。
容器适配器(Container Adaptors):利用序列容器实现特定接口的容器。包括stack、queue和priority_queue。
学习容器时,可以从以下几点入手:
容器的基本操作:插入、删除、访问元素
容器的迭代器:如何使用迭代器遍历和操作容器中的元素
容器的特性:比如vector的动态扩展能力,set的元素唯一性和有序性
容器部分主要由头文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>组成。对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。
- vector(向量)
概念:vector就像一个能够增长的数组,你可以在数组的尾部高效地添加或删除元素。
用途:当你需要一个可变大小的数组,或者你只需要在一端添加或删除元素时。
示例:
std::vector<int> vec;
vec.push_back(10); // 向vec的尾部添加一个元素
vec.push_back(20);
std::cout << vec[1]; // 打印: 20
下面给出一个完整的简单例子,演示了如何创建vector容器、插入元素并遍历容器中的元素:
cpp
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector;
// 在向量末尾插入元素
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);
// 遍历向量并输出元素
for (int i = 0; i < myVector.size(); i++) {
std::cout << myVector[i] << " ";
}
return 0;
}
输出:
1 2 3
- list(列表)
概念:list是一个双向链表,允许从两端快速添加或删除元素,但是随机访问较慢。
用途:当你需要频繁在列表的中间插入或删除元素时。
示例:
std::list<int> lst;
lst.push_back(10);
lst.push_front(20); // 在list的前面添加一个元素
下面给出一个完整的简单例子,演示了如何创建list容器、插入元素并遍历容器中的元素:
cpp
#include <iostream>
#include <list>
int main() {
std::list<int> myList;
// 在列表末尾插入元素
myList.push_back(1);
myList.push_back(2);
myList.push_back(3);
// 遍历列表并输出元素
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
输出:
1 2 3
- deque(双端队列)
概念:deque支持从头部和尾部高效地添加或删除元素。
用途:当你需要一个队列,并且想要在两端都能快速添加或删除数据时。
示例:
std::deque<int> deq;
deq.push_back(10);
deq.push_front(20); // 在deque的前面添加一个元素
下面给出一个完整的简单例子,演示了如何创建deque容器、插入元素并遍历容器中的元素:
cpp
#include <iostream>
#include <deque>
int main() {
std::deque<int> myDeque;
// 在双端队列末尾插入元素
myDeque.push_back(1);
myDeque.push_back(2);
myDeque.push_back(3);
// 在双端队列开头插入元素
myDeque.push_front(0);
// 遍历双端队列并输出元素
for (int i = 0; i < myDeque.size(); i++) {
std::cout << myDeque[i] << " ";
}
return 0;
}
输出:
0 1 2 3
- map(映射)
概念:map是一个键值对集合,它是按照键的顺序排序的,每个键只能出现一次。
用途:当你需要根据键快速查找值时,如字典。
示例:
std::map<std::string, int> scores;
scores["Alice"] = 90;
scores["Bob"] = 85; // 通过key "Bob" 设置对应的值为85
下面给出一个完整的简单例子,演示了如何创建map容器、插入元素并遍历容器中的元素:
cpp
#include <iostream>
#include <map>
int main() {
std::map<int, std::string> myMap;
// 插入键值对
myMap[1] = "apple";
myMap[2] = "banana";
myMap[3] = "orange";
// 遍历映射并输出键值对
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}
return 0;
}
输出:
Key: 1, Value: apple
Key: 2, Value: banana
Key: 3, Value: orange
- set(集合)
概念:set是一个包含唯一元素的集合,自动为你排序。
用途:当你需要存储无重复值的集合,并且希望自动按顺序排列时。
示例:
std::set<int> nums;
nums.insert(10);
nums.insert(20);
nums.insert(10); // 10已存在,不会被加入
下面给出一个完整的简单例子,演示了如何创建set容器、插入元素并遍历容器中的元素:
cpp
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
// 插入元素
mySet.insert(3);
mySet.insert(1);
mySet.insert(2);
// 遍历集合并输出元素
for (auto it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
输出:
1 2 3
迭代器( Iterators )
迭代器,是与STL容器交互的主要方式。被设计用于访问容器中的元素,类似于指针。根据功能不同,迭代器分为五种类型:输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。STL容器提供了相应的迭代器,以实现对其元素的遍历和操作。
了解迭代器的分类及其使用方法:
输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器
练习如何使用迭代器遍历容器,并结合算法实现复杂操作
STL中有多种类型的迭代器,每种迭代器都有不同的功能和用途:
输入迭代器(Input Iterator):只读访问容器中的元素,主要用于从容器中读取数据。
输出迭代器(Output Iterator):只写访问容器中的元素,主要用于向容器中写入数据。
前向迭代器(Forward Iterator):可以读写元素,并且可以在容器中向前遍历。
双向迭代器(Bidirectional Iterator):除了前向迭代的能力外,还可以向后遍历。
随机访问迭代器(Random Access Iterator):支持双向迭代,并且可以直接跳到容器中的任何元素,类似于指针。
常见的迭代器操作
迭代器有一些常用的操作方法,这些方法类似于指针的操作:
*it:解引用迭代器,访问其指向的元素。
it->member:访问迭代器指向元素的成员。
++it 或 it++:将迭代器移动到下一个元素。
--it 或 it--:将迭代器移动到前一个元素(仅限双向迭代器和随机访问迭代器)。
it1 == it2:判断两个迭代器是否指向同一个元素。
it1 != it2:判断两个迭代器是否指向不同的元素。
在C++中,迭代器(iterator)提供了一种方法,使得我们能够顺序访问一个容器(如数组、列表、向量等)中的各个元素,而不需要了解容器底层的具体实现。
通过使用迭代器,你可以遍历容器并访问其中的元素,而不需要关心底层数据结构的具体实现细节。迭代器提供了一组通用的操作接口,如移动到下一个元素、获取当前元素的值等。这样,你可以使用相同的代码逻辑来处理不同类型的容器,提高了代码的可复用性和灵活性。
需要注意的是,不同类型的容器可能具有不同类型的迭代器,因为它们的内部结构和访问方式可能不同。因此,在使用迭代器时,你需要根据具体的容器类型选择相应的迭代器类型。
下面是一些常见的C++容器及其对应的迭代器类型的示例:
数组(Array):对于普通的C数组,可以使用指针作为迭代器来访问元素。例如:
cpp
#include <iostream>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int* it = arr; // it 是指向数组首元素的指针(迭代器)
// 使用指针作为迭代器遍历数组
for (; it != arr + sizeof(arr) / sizeof(arr[0]); ++it) {
std::cout << *it << ' ';
}
std::cout << std::endl;
return 0;
}
这个示例中,it 是一个指向数组 arr 首元素的指针,它被用作迭代器来遍历数组。循环条件 it != arr + sizeof(arr) / sizeof(arr[0]) 用来确保不会超出数组的边界。sizeof(arr) / sizeof(arr[0]) 计算了数组的元素数量,arr + 数组元素数量 则指向数组的末尾之后的位置。
输出:
1 2 3 4 5
标准库容器(Standard Library Containers):不同的标准库容器有不同类型的迭代器。
std::vector:使用随机访问迭代器(random access iterator),可以通过指针或下标进行元素访问。例如:
cpp
#include <iostream>
#include <vector>
int main() {
// 创建一个 std::vector 并初始化
std::vector<int> vec = {1, 2, 3, 4, 5};
// 获取指向 vector 首元素的迭代器
std::vector<int>::iterator it = vec.begin();
// 使用迭代器遍历 vector
for (; it != vec.end(); ++it) {
std::cout << *it << ' '; // 使用 * 操作符解引用迭代器以访问元素
}
std::cout << std::endl;
// 使用随机访问迭代器的特性
// 直接访问第3个元素(注意:索引从0开始)
std::cout << "The 3rd element is: " << vec[2] << std::endl;
// 使用迭代器进行随机访问
// 注意:迭代器也可以像指针一样进行算术运算
it = vec.begin() + 2; // 将迭代器移动到第3个元素
std::cout << "The 3rd element using iterator is: " << *it << std::endl;
// 反向迭代器(reverse iterator)示例
// 使用 rbegin() 和 rend() 获取反向迭代器
std::cout << "Elements in reverse order: ";
std::vector<int>::reverse_iterator rit = vec.rbegin();
for (; rit != vec.rend(); ++rit) {
std::cout << *rit << ' ';
}
std::cout << std::endl;
return 0;
}
输出:
1 2 3 4 5
The 3rd element is: 3
The 3rd element using iterator is: 3
Elements in reverse order: 5 4 3 2 1
std::list:使用双向迭代器(bidirectional iterator),只支持前移和后移操作。例如:
cpp
#include <iostream>
#include <list>
int main() {
// 创建一个 std::list 并初始化
std::list<int> lst = {1, 2, 3, 4, 5};
// 获取指向 list 首元素的迭代器
std::list<int>::iterator it = lst.begin();
// 使用迭代器遍历 list
std::cout << "Elements in forward order: ";
for (; it != lst.end(); ++it) {
std::cout << *it << ' '; // 使用 * 操作符解引用迭代器以访问元素
}
std::cout << std::endl;
// 使用双向迭代器的特性向后遍历
// 先将迭代器指向 list 的尾元素
it = lst.end();
// 注意:end() 返回的迭代器指向最后一个元素之后的位置
// 因此,我们需要先递减迭代器以访问最后一个元素
if (it != lst.begin()) {
--it; // 将迭代器向后移动一个位置
std::cout << "Elements in backward order: ";
// 使用 -- 操作符将迭代器向后移动
for (; it != lst.begin(); --it) {
std::cout << *it << ' ';
}
std::cout << *lst.begin(); // 打印第一个元素
std::cout << std::endl;
}
return 0;
}
在上面的示例中,我们首先使用正向迭代器遍历 std::list 中的所有元素。然后,我们使用双向迭代器的特性来向后遍历列表。注意,lst.end() 返回的迭代器指向列表末尾的下一个位置(即不指向任何有效元素),因此在开始向后遍历之前,我们需要先递减迭代器以指向最后一个元素。
还要注意的是,在向后遍历的循环中,我们使用 it != lst.begin() 作为循环条件,因为当迭代器到达列表的开始时,我们不能再递减它(这会导致迭代器无效)。因此,在循环结束后,我们需要单独打印出列表的第一个元素。
双向迭代器不支持随机访问,所以你不能使用 it + n 这样的表达式来直接跳转到列表中的第 n 个元素。你只能通过连续地递增或递减迭代器来遍历列表。
输出:
Elements in forward order: 1 2 3 4 5
Elements in backward order: 5 4 3 2 1
上面这些示例只是展示了一部分常见容器的迭代器类型,实际上C++标准库提供了多种容器和对应的迭代器,每种容器都有不同的特点和适用场景。
上面这些示例只是展示了一部分常见容器的迭代器类型,实际上C++标准库提供了多种容器和对应的迭代器,每种容器都有不同的特点和适用场景。
算法( Algorithms )
提供了一系列执行各种操作(如排序、搜索、修改、复制、删除等)的函数模板。这些算法是泛型的,能够与STL容器和迭代器一起工作
STL提供了大量的算法,理解并使用这些算法能大大提高编程效率。<algorithm>头文件包含了多种用于操作序列(例如数组、向量等)的通用算法,如:
排序算法如:sort、stable_sort
搜索算法如:find、binary_search
等等
<algorithm> 头文件中一些常用的算法很多,这些算法通常与迭代器一起使用,可以应用于任何支持迭代器的容器,如 std::vector、std::list、std::array 等
STL中的算法和容器都是经过优化的,可以充分利用计算机的性能。使用STL可以大大减少代码量,使代码更加简洁易读。由于STL的广泛使用和成熟的实现,其错误率相对较低,有助于减少调试时间。熟练掌握STL的使用可以大大提高编程效率和代码质量。
例1、对vector进行排序
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> nums = {5, 3, 1, 4, 2};
// 使用STL的sort算法对vector进行排序
std::sort(nums.begin(), nums.end());
// 遍历并输出排序后的vector
for (int num : nums) {
std::cout << num << ' ';
}
std::cout << std::endl;
return 0;
}
输出:
1 2 3 4 5
例 2、搜索vector中的元素
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5};
// 使用std::find查找元素
auto it = std::find(myVector.begin(), myVector.end(), 5);
if (it != myVector.end()) {
std::cout << "Found 5 at position: " << std::distance(myVector.begin(), it) << std::endl;
} else {
std::cout << "5 not found" << std::endl;
}
return 0;
}
输出:
Found 5 at position: 4
附录
C++之STL(标准模板库)介绍https://blog.csdn.net/cnds123/article/details/118765133
各种C++ STL容器全解析 https://www.cnblogs.com/fusiwei/p/11823234.html