小白:大牛您好,我即将毕业需要找工作,但是我对于常用容器的特点和用法不是很清楚,能否跟我聊聊呢?
大牛:没问题,我很乐意帮助你。常用容器有 vector、list、set、map 和 priority_queue。其中,vector、list、set 和 map 是常用的基础容器,而 priority_queue 则是一个高级容器,主要用于实现优先队列。
小白:那么它们的特点和用法分别是什么呢?
大牛:vector 是一个动态数组,支持随机访问和快速插入和删除。list 是一个双向链表,支持快速插入和删除,但是不支持随机访问。set 是一个有序集合,支持快速查找和插入,但是不支持重复元素。map 是一个有序键值对集合,支持通过键快速查找值,但是不支持重复键。而 priority_queue 则是一个基于堆的优先队列,支持快速插入和获取队列中的最大元素。
小白:听起来好像很厉害的样子,可以给我一些具体的案例来帮助我理解吗?
大牛:当然可以。假设你需要实现一个程序,用于记录学生成绩,并能够根据成绩排名。你可以使用 vector 来存储学生信息,使用 sort 函数来对学生成绩进行排序。另外,如果你需
要支持快速删除学生信息,你可以使用 list 来代替 vector。
小白:那么如果我需要实现一个程序,用于统计文章中单词的出现次数,应该使用哪种容器?
大牛:你可以使用 map 来实现。把每个单词作为键,把单词出现的次数作为值。每次读取一个单词,就在 map 中增加对应单词的计数值。这样就可以统计文章中单词的出现次数了。
小白:那么 priority_queue 底层是如何实现的呢?
大牛:priority_queue 是一种用于实现优先队列的容器,它的底层实现通常是使用堆(heap)数据结构。
小白:堆?我还不太了解。
大牛:堆是一种特殊的树形数据结构,它满足两个条件:堆的根节点是堆中所有元素中的最大值或最小值;堆中任意节点的值总是大于等于(或小于等于)其子节点的值。这种满足条件的堆被称为大根堆或小根堆。
小白:我大概理解了,那么 priority_queue 底层是如何使用堆来实现的呢?
大牛:priority_queue 通常使用 STL 中的 vector 或 deque 作为底层容器,并且默认情况下是使用大根堆。在插入元素时,会将元素放在底层容器的末尾,然后进行上浮操作,将元素与其父节点比较,如果比父节点大,则将其与父节点交换位置,直到满足堆的条件为止。在弹出元素时,会将堆顶元素与底层容器的末尾元素交换位置,然后进行下沉操作,将元素与其子节点比较,如果比子节点小,则将其与子节点交换位置,直到满足堆的条件为止。
下面是一个使用 priority_queue 的示例代码:
#include <iostream>
#include <queue>
using namespace std;
int main() {
priority_queue<int> pq;
pq.push(5);
pq.push(3);
pq.push(8);
pq.push(1);
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
return 0;
}
还有这个,下面是一个使用 priority_queue 实现最小元素堆的例子:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
vector<int> nums = { 5, 8, 2, 10, 3 };
priority_queue<int, vector<int>, greater<int>> pq;
// 将 vector 中的元素插入 priority_queue 中
for (auto num : nums) {
pq.push(num);
}
// 从 priority_queue 中取出最小元素并打印
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
return 0;
}
在这个例子中,我们首先定义了一个 vector 存储整数,并且定义了一个 priority_queue 来存储这些整数。由于我们希望得到最小元素,我们在定义 priority_queue 时使用了 greater<int> 作为比较器。接下来,我们将 vector 中的元素逐个插入 priority_queue 中,然后从 priority_queue 中取出最小元素并打印。最终的输出结果应该是:2 3 5 8 10。
小白:非常感谢您的讲解,我对常用容器的特点和用法以及 priority_queue 的底层实现有了更深入的了解。
小白:好的,我会的。不过,我还有一个问题,您能不能再讲讲 STL 中其他的常用容器?
大牛:当然可以。除了 vector、list、deque 和 priority_queue,还有很多其他的容器。比如:
- set 和 multiset:用于存储不重复的元素,并且可以按照一定规则进行排序。
- map 和 multimap:用于存储键值对,并且可以按照键进行排序。map 中的键是唯一的,而 multimap 中的键可以重复。
- unordered_set 和 unordered_multiset:与 set 和 multiset 类似,但是不保证元素的顺序,而是使用哈希表进行快速查找。
- unordered_map 和 unordered_multimap:与 map 和 multimap 类似,但是不保证元素的顺序,而是使用哈希表进行快速查找。
小白:听起来很多啊,这些容器都有什么特点和用法呢?
大牛:set 和 map 是有序的容器,使用红黑树作为底层实现。set 可以用于快速查找一个元素是否存在,map 可以用于快速查找一个键对应的值。multiset 和 multimap 可以存储重复的元素或键。这些容器的插入、查找和删除操作的时间复杂度都是 O(log n)。
unordered_set 和 unordered_map 是无序的容器,使用哈希表作为底层实现。它们的插入、查找和删除操作的时间复杂度都是 O(1),但是在遍历容器时,元素的顺序是不确定的。如果需要存储无序的元素或键值对,并且需要快速查找,那么就可以选择使用 unordered_set 和 unordered_map。
multiset、multimap、unordered_multiset 和 unordered_multimap 的使用方法和 set、map、unordered_set 和 unordered_map 相似,只是在插入、查找和删除元素时需要注意重复元素或键的情况。
小白:哇,听起来好复杂啊。不过,我会尝试去理解和使用它们的。谢谢您的讲解。
大牛:不用谢,你可以多看看 STL 的文档和一些实际的例子,这样会更容易理解。如果有问题,随时来问我。