本文最后更新于 34 天前,其中的信息可能已经有所发展或是发生改变。
一、为什么要用嵌套容器?(先理解需求)
生活中的例子
想象你需要记录这些东西:
- 一个班的成绩表:3个学生,每人有5门课成绩
- 一个学校的课程表:5个年级,每个年级有若干班级
- 一个城市的电话号码簿:按区号分类,每个区号下有多个号码
这些都需要表格形式或多级分类的存储,这时就需要嵌套容器。
二、从简单到复杂
第1步:先复习普通vector
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 1. 普通的一维vector - 存储一组数字
vector<int> scores = {90, 85, 95, 88};
// 2. 访问单个元素
cout << "第一个成绩:" << scores[0] << endl; // 90
// 3. 遍历
cout << "所有成绩:";
for (int i = 0; i < scores.size(); i++) {
cout << scores[i] << " ";
}
// 输出:90 85 95 88
}
第2步:思考问题 – 多个学生的成绩
// 假设有3个学生,每个学生有3门课成绩
// 错误做法:用3个独立的vector
vector<int> student1_scores = {90, 85, 88};
vector<int> student2_scores = {78, 92, 85};
vector<int> student3_scores = {88, 79, 94};
// 问题:不好统一管理,不能循环处理所有学生
三、引入二维vector(嵌套vector)
基本概念理解
二维vector就像表格:
┌─────────┬─────────┬─────────┐
│ 学生1 │ 90 │ 85 │ 88 │
├─────────┼─────────┼─────────┤
│ 学生2 │ 78 │ 92 │ 85 │
├─────────┼─────────┼─────────┤
│ 学生3 │ 88 │ 79 │ 94 │
└─────────┴─────────┴─────────┘
在内存中:
outer_vec[0] → {90, 85, 88} ← 第0行,学生1的成绩
outer_vec[1] → {78, 92, 85} ← 第1行,学生2的成绩
outer_vec[2] → {88, 79, 94} ← 第2行,学生3的成绩
第3步:创建二维vector
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 方法1:先创建外层vector,再逐个添加内层vector
vector<vector<int>> all_students_scores; // 空的二维vector
// 创建每个学生的成绩vector
vector<int> student1 = {90, 85, 88};
vector<int> student2 = {78, 92, 85};
vector<int> student3 = {88, 79, 94};
// 添加到外层vector
all_students_scores.push_back(student1);
all_students_scores.push_back(student2);
all_students_scores.push_back(student3);
cout << "共有" << all_students_scores.size() << "个学生" << endl; // 3
// 访问第一个学生的第一门成绩
cout << "学生1的第1门成绩:" << all_students_scores[0][0] << endl; // 90
}
第4步:简化创建方法
// 方法2:直接初始化(推荐)
vector<vector<int>> scores = {
{90, 85, 88}, // 第0个学生的成绩
{78, 92, 85}, // 第1个学生的成绩
{88, 79, 94} // 第2个学生的成绩
};
// 一行代码搞定,清晰直观!
四、如何遍历二维vector?
方法1:双重循环(最容易理解)
vector<vector<int>> scores = {
{90, 85, 88},
{78, 92, 85},
{88, 79, 94}
};
// 外层循环:遍历每个学生
for (int i = 0; i < scores.size(); i++) {
cout << "学生" << i + 1 << "的成绩:";
// 内层循环:遍历这个学生的每门课
for (int j = 0; j < scores[i].size(); j++) {
cout << scores[i][j] << " ";
}
cout << endl;
}
输出:
学生1的成绩:90 85 88
学生2的成绩:78 92 85
学生3的成绩:88 79 94
方法2:范围for循环(C++11,更简洁)
int student_num = 1;
for (const vector<int>& student_scores : scores) { // 遍历每个学生
cout << "学生" << student_num << "的成绩:";
for (int score : student_scores) { // 遍历这个学生的成绩
cout << score << " ";
}
cout << endl;
student_num++;
}
五、理解索引访问
vector<vector<int>> scores = {
{90, 85, 88}, // scores[0]
{78, 92, 85}, // scores[1]
{88, 79, 94} // scores[2]
};
// 访问方式:scores[行][列]
cout << scores[0][0] << endl; // 90
cout << scores[1][2] << endl; // 85
cout << scores[2][1] << endl; // 79
// 理解:
// scores[0] 获取第一个内层vector {90, 85, 88}
// scores[0][1] 在这个内层vector中取第2个元素 85
六、常见操作
1. 添加新的学生
vector<vector<int>> scores = {
{90, 85, 88},
{78, 92, 85}
};
// 添加一个学生
vector<int> new_student = {95, 87, 91};
scores.push_back(new_student);
// 更简洁的写法
scores.push_back({82, 76, 89}); // 直接添加
2. 修改成绩
// 修改第2个学生的第3门成绩
scores[1][2] = 90; // 原来是85,改为90
// 添加一门新课的成绩(给所有学生)
for (int i = 0; i < scores.size(); i++) {
scores[i].push_back(80 + i * 5); // 假设新成绩
}
3. 删除学生
// 删除第2个学生(索引1)
scores.erase(scores.begin() + 1);
// 删除第一个学生的第2门成绩
scores[0].erase(scores[0].begin() + 1);
七、不同形状的二维vector
重要特点:每行的长度可以不同!
// 不规则二维数组 - 每个学生课程数不同
vector<vector<int>> irregular_scores = {
{90, 85, 88, 92}, // 学生1有4门课
{78, 92}, // 学生2有2门课
{88, 79, 94, 87, 91} // 学生3有5门课
};
// 查看每个学生有多少门课
for (int i = 0; i < irregular_scores.size(); i++) {
cout << "学生" << i+1 << "有" << irregular_scores[i].size()
<< "门课" << endl;
}
八、实际案例:成绩统计系统
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
// 学生姓名和成绩
vector<string> names = {"张三", "李四", "王五"};
// 每个学生的成绩(三门课)
vector<vector<int>> scores = {
{90, 85, 88}, // 张三的成绩
{78, 92, 85}, // 李四的成绩
{88, 79, 94} // 王五的成绩
};
// 计算每个学生的平均分
cout << "====== 学生成绩统计 ======\n";
for (int i = 0; i < names.size(); i++) {
cout << names[i] << "的成绩:";
int sum = 0;
for (int j = 0; j < scores[i].size(); j++) {
cout << scores[i][j] << " ";
sum += scores[i][j];
}
double average = static_cast<double>(sum) / scores[i].size();
cout << " 平均分:" << average << endl;
}
}
九、三维vector(了解)
// 理解三维:多个班级,每个班级有多个学生,每个学生有多门成绩
vector<vector<vector<int>>> all_classes_scores = {
// 班级1
{
{90, 85, 88}, // 学生1
{78, 92, 85} // 学生2
},
// 班级2
{
{88, 79, 94}, // 学生1
{76, 85, 89}, // 学生2
{92, 88, 90} // 学生3
}
};
// 访问:班级[学生][课程]
cout << "班级1,学生2,第3门成绩:"
<< all_classes_scores[0][1][2] << endl; // 85
十、总结和练习
关键点总结
- 嵌套vector = vector里面存放vector
- 语法:
vector<vector<类型>> 变量名 - 访问:
变量名[行][列] - 每行长度可以不同,这是与二维数组的最大区别
- 遍历:用双重循环
练习题
- 创建一个3×3的矩阵,并初始化为:
1 2 3
4 5 6
7 8 9
然后计算对角线元素的和。
- 创建一个记录5个学生考试成绩的二维vector,每个学生参加考试的科目数不同,然后计算每个学生的总分。
- 创建一个三角形的数字金字塔,使用嵌套vector存储:
1
2 3
4 5 6
7 8 9 10
参考答案
// 练习1
vector<vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int sum = matrix[0][0] + matrix[1][1] + matrix[2][2];
cout << "对角线之和:" << sum << endl; // 15
记住:从一维到二维,就是把每个元素从单个值变成了一组值。多练习几次,就会很自然了!