C++刷题杂记
C++中如何声明二维vector
在C++中,你可以使用嵌套的std::vector
来声明一个二维的vector。每个元素本身是一个std::vector
,而这些元素的集合构成了外部的std::vector
。以下是如何声明一个二维vector的示例:
#include <vector>
int main() {
// 声明一个二维vector,初始化为3行,每行有0个元素
std::vector<std::vector<int>> matrix(3);
// 如果你还想初始化每行的列数,你可以使用循环或者列表初始化
// 使用循环为每行分配列
for (int i = 0; i < matrix.size(); ++i) {
matrix[i].resize(2); // 现在每行有2个元素
}
// 或者在声明时就指定每行的列数(使用列表初始化)
std::vector<std::vector<int>> matrix2 = {
{0, 0}, // 第一行有2个元素
{0, 0}, // 第二行有2个元素
{0, 0} // 第三行有2个元素
};
// 或者在声明时使用构造函数和默认元素数量
std::vector<std::vector<int>> matrix3(3, std::vector<int>(2, 0));
// matrix3是一个3x2的二维vector,所有元素都初始化为0
return 0;
}
在上面的代码中,matrix
是一个二维vector,最初被声明为包含3个std::vector<int>
对象,但这些对象最初是空的。然后,我们使用循环来为每个内部vector分配2个元素的空间。
matrix2
是使用列表初始化来声明的,它直接在声明时指定了每行的元素数量和值。
matrix3
是在声明时通过构造函数来指定行数和列数的,并且为所有元素提供了一个默认值(在这个例子中是0)。这是声明和初始化二维vector时最常用的方法之一,因为它既设置了大小又提供了元素的初始值。
map初始化和遍历
在C++中,std::map
是一个关联容器,它存储的元素是键值对(key-value pairs),并且按键进行排序。每个元素包含一个键和一个值,键在容器中是唯一的。
初始化
std::map
可以通过多种方式进行初始化:
- 默认初始化
std::map<int, std::string> myMap;
这将创建一个空的 map
,键的类型为 int
,值的类型为 std::string
。
2. 使用初始化列表
C++11 引入了初始化列表,这使得初始化 map
变得更加简洁:
std::map<int, std::string> myMap = {
{1, "one"},
{2, "two"},
{3, "three"}
};
- 使用
insert
方法
你也可以在创建 map
后使用 insert
方法来添加元素:
std::map<int, std::string> myMap;
myMap.insert(std::pair<int, std::string>(1, "one"));
myMap.insert(std::pair<int, std::string>(2, "two"));
// 或者使用 `make_pair`
myMap.insert(std::make_pair(3, "three"));
// C++11 之后还可以使用 `{}`
myMap.insert({4, "four"});
- 使用
emplace
方法
emplace
方法可以在容器内直接构造元素,这通常比 insert
更高效:
std::map<int, std::string> myMap;
myMap.emplace(1, "one");
myMap.emplace(2, "two");
遍历
遍历 std::map
的常见方法是使用迭代器或基于范围的 for
循环。
- 使用迭代器
你可以使用迭代器来遍历 map
中的所有元素:
std::map<int, std::string> myMap = {
{1, "one"},
{2, "two"},
{3, "three"}
};
for (std::map<int, std::string>::iterator it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}
在C++11及更高版本中,你还可以使用 auto
关键字来简化迭代器的类型声明:
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
}
- 使用基于范围的
for
循环(C++11及更高版本)
基于范围的 for
循环提供了一种更简洁、更易读的方式来遍历容器:
for (const auto& pair : myMap) {
std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}
这里,const auto&
用于避免不必要的拷贝,并确保我们不会意外地修改容器中的元素。如果你需要修改元素,可以省略 const
。
isalpha、isspace、isdigit、ispunct
在C和C++的<ctype.h>
(C)或<cctype>
(C++)库中,有几个用于字符分类的函数,它们可以检查一个字符是否属于特定的字符类别。这些函数通常用于文本处理和解析。以下是您提到的几个函数的简要说明:
-
isalpha()
- 检查字符是否是字母(无论是大写还是小写)。
- 对于ASCII字符集,这包括从
A
到Z
和从a
到z
的字符。 - 如果字符是字母,则返回非零值(真);否则返回0(假)。
-
isspace()
- 检查字符是否是空白字符。
- 空白字符包括空格(' ')、制表符('\t')、换行符('\n')、回车符('\r')、垂直制表符('\v')、换页符('\f')等。
- 如果字符是空白字符,则返回非零值(真);否则返回0(假)。
-
isdigit()
- 检查字符是否是十进制数字。
- 对于ASCII字符集,这包括从
0
到9
的字符。 - 如果字符是数字,则返回非零值(真);否则返回0(假)。
-
ispunct()
- 检查字符是否是标点符号。
- 标点符号包括所有非字母、非数字、非空白的可打印ASCII字符,如逗号、句号、引号、括号等。
- 如果字符是标点符号,则返回非零值(真);否则返回0(假)。
这些函数通常用于条件语句中,以便根据字符的类型执行不同的操作。例如,您可以遍历一个字符串,并使用这些函数来统计其中字母、数字、空白字符和标点符号的数量。
set容器的性质
-
自动排序:
std::set
内部使用红黑树(一种自平衡的二叉搜索树)来存储元素。由于二叉搜索树的性质,元素在树中按照升序排列。因此,当你向set
中插入元素时,它们会自动按照升序排序。- 请注意,这里的排序是基于元素类型的“小于”操作符(
<
)进行的。你可以通过为set
提供一个自定义的比较函数或对象来改变排序行为。
-
元素的唯一性:
std::set
保证容器中的每个元素都是唯一的。如果你尝试插入一个已经存在于set
中的元素,那么新的插入操作将不会有任何效果(即,不会插入重复的元素,也不会替换现有的元素)。- 元素的唯一性也是通过“小于”操作符或自定义的比较函数/对象来确定的。两个元素被认为是相同的,如果它们的比较结果为“不相等”(即,既不大于也不小于)。
这些性质使得std::set
在需要快速查找、插入和删除操作,同时保持元素有序和唯一的应用场景中非常有用。
示例
std::set
在C++中常用于需要保持元素唯一性且自动排序的场景。下面是一个简单的例子,展示了如何使用std::set
来存储和检索一组不重复的整数,并保持它们的有序性:
#include <iostream>
#include <set>
int main() {
// 创建一个空的set容器
std::set<int> mySet;
// 向set中插入元素
mySet.insert(5);
mySet.insert(2);
mySet.insert(8);
mySet.insert(1);
mySet.insert(5); // 重复插入,不会有效果
// 遍历set并打印元素
for (int num : mySet) {
std::cout << num << " ";
}
std::cout << std::endl;
// 检查元素是否存在于set中
int searchValue = 8;
if (mySet.find(searchValue) != mySet.end()) {
std::cout << "元素 " << searchValue << " 存在于set中" << std::endl;
} else {
std::cout << "元素 " << searchValue << " 不存在于set中" << std::endl;
}
// 删除元素
searchValue = 2;
mySet.erase(searchValue);
// 再次遍历set并打印元素
std::cout << "删除元素后的set: ";
for (int num : mySet) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
输出将是:
1 2 5 8
元素 8 存在于set中
删除元素后的set: 1 5 8
在这个例子中,我们首先创建了一个空的std::set<int>
,然后插入了几个整数。注意到即使我们尝试插入了一个重复的数字5,它也只会出现一次,因为set
保证了元素的唯一性。接着我们使用范围for循环遍历set
并打印出其中的元素,可以看到它们已经按照升序排列。之后我们使用find
方法检查一个特定的值是否存在于set
中,并使用erase
方法删除一个元素。最后我们再次遍历set
以展示删除操作后的结果。
unordered_set
unordered_set
是C++标准库中的一个关联容器,它使用哈希表来实现元素的存储。以下是unordered_set
容器的主要性质:
-
无序性:与
set
容器不同,unordered_set
不保证元素的任何特定顺序。元素在unordered_set
中的位置取决于它们的哈希值,而不是它们的比较顺序。 -
唯一性:
unordered_set
容器中的元素是唯一的。尝试插入一个已经存在的元素将不会有任何效果,因为容器不允许重复元素。 -
高效的查找、插入和删除:由于
unordered_set
使用哈希表来存储元素,因此它的查找、插入和删除操作在平均情况下具有常数时间复杂度O(1)。但是,在最坏的情况下,这些操作的时间复杂度可能会达到O(n),其中n是容器中元素的数量。这通常发生在哈希冲突非常多的时候。 -
使用哈希函数和相等比较:
unordered_set
使用一个哈希函数来计算元素的哈希值,并使用一个相等比较函数来确定两个元素是否相等。默认情况下,这些函数是基于元素类型的operator==
和std::hash
特化来定义的,但你可以提供自定义的函数或对象。 -
不支持下标运算符:与
std::map
和std::unordered_map
不同,unordered_set
不支持使用下标运算符(operator[]
)来访问元素,因为它只存储键,而不存储与键相关联的值。 -
迭代器是前向迭代器:
unordered_set
提供的迭代器至少是前向迭代器,这意味着你可以使用它们来遍历容器中的元素,但不能进行反向遍历(除非你显式地保存了反向迭代器)。然而,在实践中,许多实现提供的迭代器实际上是双向迭代器。
请注意,由于哈希表的实现细节和哈希冲突的处理方式,unordered_set
的实际性能可能会因库的实现、哈希函数的质量以及数据的分布等因素而有所不同。在选择使用unordered_set
还是其他容器(如set
、vector
或map
)时,应该基于你的具体需求和性能目标来做出决策。
示例
当然,下面是一个使用std::unordered_set
的简单例子,展示了如何插入、查找和删除元素:
#include <iostream>
#include <unordered_set>
int main() {
// 创建一个空的unordered_set容器
std::unordered_set<int> myUnorderedSet;
// 向unordered_set中插入元素
myUnorderedSet.insert(5);
myUnorderedSet.insert(2);
myUnorderedSet.insert(8);
myUnorderedSet.insert(1);
myUnorderedSet.insert(5); // 尝试重复插入,不会有效果
// 查找元素是否存在
int searchValue = 8;
if (myUnorderedSet.find(searchValue) != myUnorderedSet.end()) {
std::cout << "元素 " << searchValue << " 存在于unordered_set中" << std::endl;
} else {
std::cout << "元素 " << searchValue << " 不存在于unordered_set中" << std::endl;
}
// 删除元素
searchValue = 2;
myUnorderedSet.erase(searchValue);
// 验证元素是否被删除
if (myUnorderedSet.find(searchValue) == myUnorderedSet.end()) {
std::cout << "元素 " << searchValue << " 已被从unordered_set中删除" << std::endl;
}
// 遍历unordered_set并打印元素
for (const auto& num : myUnorderedSet) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
输出将是(注意,由于unordered_set是无序的,所以输出的顺序可能会有所不同):
元素 8 存在于unordered_set中
元素 2 已被从unordered_set中删除
1 5 8
在这个例子中,我们首先创建了一个空的std::unordered_set<int>
,然后插入了几个整数。注意到即使我们尝试插入了一个重复的数字5,它也只会出现一次,因为unordered_set
保证了元素的唯一性。接着我们使用find
方法检查一个特定的值是否存在于unordered_set
中,并使用erase
方法删除一个元素。之后我们再次使用find
来验证元素确实被删除了。最后,我们使用范围for循环遍历unordered_set
并打印出其中的元素。由于unordered_set
是无序的,因此每次运行程序时打印出的元素顺序可能会有所不同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)