吊打面试官 | C++ STL 二维vector的写法,先行再列和先列再行遍历

名企高频考点之-C++ STL 二维vector的写法,先行再列和先列再行遍历

0. 概述

二维数组是日常开发中使用高频的一种管理数据的方式,比如迷宫地图,邻接矩阵等,操作起来也非常方便。在面试中也经常被问到,本文主要对vector构造的二维数组进行说明。

1. 传统二维数组的缺陷

传统定义二维数组的方式,采用宏定义给出二维数组的行和列,然后定义出二维数组并对其进行初始化,最后就是对该二维数组进行操作,比如:

#define ROW 5
#define COL 5

void 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<size_t n, const V& v> 来进行构造

    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行:1
1行:1 1
2行:1 2 1
3行:1 3 3 1
4行: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<<vv[i][j]<<" ";
		}
		cout << endl;
	}
}

在这里插入图片描述

2.3 不确定行列个数

该种场景也比较常见,从问题给的描述中只知道结果为二维数组,但是二维数组的行列需要根据题目的输入来确定,直接无法确定。

此种方式的常见解法是:先构造一个空的二维数组,然后构造一个一维的不断向二维数组中插入

例如:二叉树的封层遍历

在这里插入图片描述

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> ret;
        if(nullptr == root)
            return ret;

        queue<TreeNode*> 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 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5

1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 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 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5

1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5

4. 总结

本文主要介绍了vector构造二维数组的常见方式以及遍历,具体如下:

在这里插入图片描述

相信大家对二维数组的使用有进一步的了解,具体还应该根据实际情况选择合适的构造方式。最后介绍了二维数组行优先遍历以及列优先的遍历方式,以及两种遍历方式的区别,希望通过本文学习,大家对于二维数组应用可以得心应手,谢谢。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值