9.1 顺序容器概述
C++标准库提供了几种顺序容器,用于存储和管理元素的有序集合。主要的顺序容器包括vector
、deque
、list
和forward_list
。每种容器都有其独特的特性和适用场景。
9.1.1 vector
vector
是一个动态数组,支持快速的随机访问和在末尾高效的插入和删除操作。它的大小可以动态调整,并且内存是连续的。
示例代码
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);
for (const auto& elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.1.2 deque
deque
(双端队列)支持在两端进行高效的插入和删除操作。它提供了类似于vector
的随机访问能力,但内存不是连续的。
示例代码
#include <iostream>
#include <deque>
int main() {
std::deque<int> deq = {1, 2, 3, 4, 5};
deq.push_front(0);
deq.push_back(6);
for (const auto& elem : deq) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.1.3 list
list
是一个双向链表,支持在任何位置进行高效的插入和删除操作,但不支持随机访问。
示例代码
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_front(0);
lst.push_back(6);
for (const auto& elem : lst) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.1.4 forward_list
forward_list
是一个单向链表,只支持单向遍历,适用于内存有限且插入删除操作频繁的场景。
示例代码
#include <iostream>
#include <forward_list>
int main() {
std::forward_list<int> flst = {1, 2, 3, 4, 5};
flst.push_front(0);
for (const auto& elem : flst) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.1.5 容器选择
选择合适的容器是编写高效程序的关键。以下是一些指导原则:
- 使用
vector
:当需要频繁随机访问元素或在末尾插入删除元素时。 - 使用
deque
:当需要在两端进行高效的插入和删除操作时。 - 使用
list
:当需要在中间进行频繁插入和删除操作且不需要随机访问时。 - 使用
forward_list
:当内存有限且只需要单向遍历时。
重点与难点分析
重点:
- 了解各种顺序容器的特性:理解每种容器的特性和适用场景,选择最合适的容器来提高程序的性能。
- 掌握基本操作:熟悉顺序容器的基本操作,如插入、删除、遍历等。
难点:
- 容器的性能权衡:在选择容器时,需要根据具体的使用场景权衡性能,例如随机访问的效率、插入删除操作的效率等。
- 容器的内存管理:理解容器在内存管理方面的差异,例如
vector
的连续内存分配和list
的非连续内存分配。
练习题解析
- 练习9.1:编写一个程序,使用
vector
存储一组整数,并输出所有元素。
-
- 示例代码:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (const auto& elem : vec) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
- 练习9.2:编写一个程序,使用
deque
存储一组整数,并在两端插入元素,输出所有元素。
-
- 示例代码:
#include <iostream>
#include <deque>
int main() {
std::deque<int> deq = {1, 2, 3, 4, 5};
deq.push_front(0);
deq.push_back(6);
for (const auto& elem : deq) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
- 练习9.3:编写一个程序,使用
list
存储一组整数,并在两端插入元素,输出所有元素。
-
- 示例代码:
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_front(0);
lst.push_back(6);
for (const auto& elem : lst) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
- 练习9.4:编写一个程序,使用
forward_list
存储一组整数,并在开头插入元素,输出所有元素。
-
- 示例代码:
#include <iostream>
#include <forward_list>
int main() {
std::forward_list<int> flst = {1, 2, 3, 4, 5};
flst.push_front(0);
for (const auto& elem : flst) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
总结与提高
本节总结:
- 了解了
vector
、deque
、list
和forward_list
等顺序容器的基本特性和适用场景。 - 掌握了这些容器的基本操作,包括插入、删除和遍历。
- 理解了如何根据具体的应用场景选择最合适的容器,以提高程序的性能。
提高建议:
- 多练习容器操作:通过编写更多涉及顺序容器的程序,熟悉各种容器的基本操作和特性。
- 优化容器选择:在实际项目中,根据具体需求选择最合适的容器,优化程序的性能。
- 深入理解容器实现:通过阅读标准库的实现代码或相关书籍,深入理解顺序容器的内部实现原理,提高编写高效代码的能力。
9.2 容器库概览
C++标准库提供了一套丰富的容器,用于存储和管理数据。每种容器都有其独特的特性和适用场景。在本节中,我们将概览一些常用的容器功能,并介绍它们的基本用法和特性。
9.2.1 迭代器
迭代器是用于遍历容器中元素的对象,类似于指针。通过迭代器,可以实现对容器中元素的访问和操作。
示例代码
#include <iostream>
#include <vector>
int main() {
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;
return 0;
}
9.2.2 容器类型成员
每个容器都有一些特定的类型成员,用于表示迭代器类型、大小类型等。例如,vector<int>::iterator
表示vector<int>
的迭代器类型。
示例代码
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int>::iterator it = vec.begin();
while (it != vec.end()) {
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
return 0;
}
9.2.3 begin和end成员
所有标准库容器都提供begin
和end
成员函数,用于获取指向容器第一个元素和最后一个元素之后位置的迭代器。
示例代码
#include <iostream>
#include <list>
int main() {
std::list<int> lst = {1, 2, 3, 4, 5};
for (auto it = lst.begin(); it != lst.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
9.2.4 容器定义和初始化
容器可以通过各种方式进行定义和初始化,包括默认构造函数、初始值列表、拷贝构造函数等。
示例代码
#include <iostream>
#include <deque>
int main() {
std::deque<int> deq1; // 默认构造函数
std::deque<int> deq2 = {1, 2, 3, 4, 5}; // 初始值列表
std::deque<int> deq3(deq2); // 拷贝构造函数
for (const auto& elem : deq3) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.2.5 赋值和swap
容器支持赋值操作,可以将一个容器的内容赋值给另一个容器。swap
函数用于交换两个容器的内容。
示例代码
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = {4, 5, 6};
vec1 = vec2; // 赋值操作
vec1.swap(vec2); // 交换操作
for (const auto& elem : vec1) {
std::cout << elem << " ";
}
std::cout << std::endl;
for (const auto& elem : vec2) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
9.2.6 关系运算符
标准库容器支持关系运算符,如==
、!=
、<
、>
、<=
和>=
。这些运算符用于比较两个容器的内容。
示例代码
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = {1, 2, 3};
std::vector<int> vec3 = {4, 5, 6};
if (vec1 == vec2) {
std::cout << "vec1 is equal to vec2" << std::endl;
} else {
std::cout << "vec1 is not equal to vec2" << std::endl;
}
if (vec1 < vec3) {
std::cout << "vec1 is less than vec3" << std::endl;
} else {
std::cout << "vec1 is not less than vec3" << std::endl;
}
return 0;
}
重点与难点分析
重点:
- 迭代器的使用:掌握如何使用迭代器遍历容器元素。
- 容器类型成员:理解容器的类型成员及其用途。
- begin和end成员:熟悉容器的
begin
和end
成员函数的用法。 - 容器的定义和初始化:掌握容器的各种初始化方式。
- 赋值和swap:理解容器的赋值操作和
swap
函数的用法。 - 关系运算符:掌握如何使用关系运算符比较容器。
难点:
- 迭代器的生命周期:理解迭代器在容器操作中的有效性,避免使用失效的迭代器。
- 容器的类型成员:掌握不同容器的类型成员及