点击蓝字关注我哦
以下是本期干货视频 视频后还附有文字版本哦 ▼ 《名企高频考点-C++ STL 二维vector的写法,先行再列和先列再行遍历》 ▼ ps:请在WiFi环境下打开,如果有钱任性请随意0. 概述
二维数组是日常开发中使用高频的一种管理数据的方式,比如迷宫地图,邻接矩阵等,操作起来也非常方便。在面试中也经常被问到,本文主要对vector构造的二维数组进行说明。1.传统二维数组的缺陷
传统定义二维数组的方式,采用宏定义给出二维数组的行和列,然后定义出二维数组并对其进行初始化,最后就是对该二维数组进行操作,比如:#define ROW 5#define COL 5void Test2Array(){ int array[ROW][COL] = { { 0, 1, 0, 0, 0 }, { 0, 1, 0, 0, 0 }, { 0, 1, 1, 1, 0 }, { 0, 1, 0, 1, 0 }, { 0, 0, 0, 1, 0 } }; // 打印二维数组 for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { cout << array[i][j] << " "; } cout << endl; } cout << endl; }
该种方式使用起来确实比较方便,但缺陷是:数组被限制死了,只能表示5行5列的数组,但有些情况下需要的可能是动态的二维数组,传统二维数组就无能为力。
2. 使用vector定义二维数组
2.1 矩阵(每行元素个数相同)
(1)元素内容都相同:直接使用vector中vector 来进行构造
int main(){ size_t row, col; int val; cin>>row>>col>>val; // 创建一个row行col列的矩阵,并使用val进行填充 vector<vector<int>> v(row, vector<int>(col, val)); return 0;}
(2)元素内容不同:先创建好矩阵,然后给每行元素依次赋值
int main(){ int row,col; cin>>row>>col; // 创建一个row行col列的二维数组,逐个给每行元素进行赋值 vector<vector<int>> v(row, vector<int>(col)); // 将每行的元素赋值为1~col for(int i = 0; i < v.size(); ++i) { for(int j = 0; j < v[i].size(); ++j) { v[i][j] = j+1; } } return 0;}
2.2 动态二维数组
先开辟行,再根据每列中具体元素个数来开辟空间以及给每行元素进行赋值,比如:杨辉三角/*杨慧三角的前5行0行:11行:1 12行:1 2 13行:1 3 3 14行:1 4 6 4 1观察发现:第0列和对角线全部为1,其余位置为上一行同列元素以及上一行同列前一个元素之和*/void PascalTriangle(int N){ vector<vector<int>> vv; // 先给出二维数组的行,此时每行还没有空间 vv.resize(N); for (int i = 0; i < N; ++i) { // 将第i行元素个数设置为i+1,初始值用1填充 vv[i].resize(i + 1, 1); for (int j = 1; j < i; ++j) { vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; } } // 打印二维数组 for (int i = 0; i < N; ++i) { for (int j = 0; j <= i; ++j) { cout<" "; } cout << endl; }}
2.3 不确定行列个数
该种场景也比较常见,从问题给的描述中只知道结果为二维数组,但是二维数组的行列需要根据题目的输入来确定,直接无法确定。 此种方式的常见解法是:先构造一个空的二维数组,然后构造一个一维的不断向二维数组中插入 例如:二叉树的封层遍历class Solution {public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> ret; if(nullptr == root) return ret; queue q; q.push(root); while(!q.empty()){ // 为了提高效率,先插入一个空的vector ret.push_back(vector<int>()); // 拿到数组的最后一行,借助引用直接在最后一行插入 vector<int>& level = ret.back(); // 本层中节点总的个数 size_t size = q.size(); for(int i = 0; i < size; ++i) { TreeNode* cur = q.front(); q.pop(); level.push_back(cur->val); if(cur->left) q.push(cur->left); if(cur->right) q.push(cur->right); } } return ret; }};
2.4 二维数组的误用
用vector创建的二维数组,一般情况下是先给出有多少行,然后再对每行进行操作。最常见的误用就是还没有给每行分配空间,就直接对行进行操作而引起代码崩溃。void Test2Vector(){ vector<vector<int>> vv; vv.resize(5); // 二维数组总共有5行,但是每行现在还没有空间 vv[0][0] = 10; // 此时直接操作每行中元素时会崩溃}
3. 面试题
二维数组先行后列遍历效率高
还是先列后行遍历效率高?
3.1 先行后列
先行后列是最常见的二维数组的遍历方式,而且效率非常高,因为二维数组的每一行都是一段连续的空间,根据局部性原理,操作系统再访问每个元素时,会将该元素附近多个元素一次性加载到缓存中来提高程序效率。void Print2Vector(){ // 采用C++11提供的列表初始化构造二维数组,每行元素使用{1,2,3,4,5}进行填充 vector<vector<int>> vv(5, { 1, 2, 3, 4, 5 }); // 常规方式 for (size_t i = 0; i < vv.size(); ++i) { for (size_t j = 0; j < vv[i].size(); ++j) { cout << vv[i][j] << " "; } cout << endl; } cout << endl; // 采用范围for打印 for (auto& rowV : vv){ for (auto e : rowV){ cout << e << " "; } cout << endl; } cout << endl;}程序输出:1 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 5
3.2 先列后行
该种方式使用比较少,因为效率比较低,一般情况下适合矩阵使用场景。
void Print2Vector2(){ // 采用C++11提供的列表初始化构造二维数组,每行元素使用{1,2,3,4,5}进行填充 vector<vector<int>> vv(5, { 1, 2, 3, 4, 5 }); // 先行后列 for (size_t row = 0; row < vv.size(); ++row) { for (size_t col = 0; col < vv[i].size(); ++col) { cout << vv[row][col] << " "; } cout << endl; } cout << endl; // 先列后行 for (size_t col = 0; col < vv[0].size(); ++col) { for (size_t row = 0; row < vv.size(); ++row) { cout << vv[row][col] << " "; } cout << endl; } cout << endl;}程序输出:1 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 51 1 1 1 12 2 2 2 23 3 3 3 34 4 4 4 45 5 5 5 5
4. 总结
本文主要介绍了vector构造二维数组的常见方式以及遍历,具体如下: 相信大家对二维数组的使用有进一步的了解,具体还应该根据实际情况选择合适的构造方式。 最后介绍了二维数组行优先遍历以及列优先的遍历方式,以及两种遍历方式的区别,希望通过本文学习,大家对于二维数组应用可以得心应手,谢谢。 作者:时亮益 审核:王海斌 编辑:比特李哥好看,就要点个"在看"