【C语言】矩阵转置

下面的内容涉及二维数组的知识,建议学习过的朋友食用哦!

前言

矩阵转置是将原矩阵的行与列进行互换所得到的新矩阵。

具体来说,给定一个m×n阶矩阵A,其转置矩阵AT是一个n×m阶矩阵,满足AT[i][j] = A[j][i],其中1≤i≤n,1≤j≤m。即转置矩阵的第i行第j列元素等于原矩阵的第j行第i列元素。

求转置矩阵的方法相对简单,只需要创建一个新的空矩阵AT,其行数与原矩阵A的列数相同,列数与原矩阵A的行数相同,然后遍历原矩阵A的每一个元素A[i][j],将其赋值给新矩阵AT的对应位置AT[j][i]即可。

这么讲完你可能还是一头雾水,没关系,接下来就由我来将矩阵转置的过程细细剖析,嚼烂了、教给你^_^

问题

现在我们先有一个二维数组arr:

#include<stdio.h>
int main()
{
	int arr[3][5] = {{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};
	int i, j;//i来控制行的输出,j控制列的输出
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%3d ", arr[i][j]);//写成%3d,让打印出来的矩阵更整齐一点
		}
		printf("\n");//每一行内容打印完一定要换行!
	}

	return 0;
}

在vs中打印效果如下: 

那么现在我们想要的无非就是变成这种效果:

说得直观点,我们就是想把上面那张图中的效果按列打印出来。原本的第一列是1 6 11,现在变成了我们的第一行,原本的第二列2 7 12,现在变成了我们的第二行……

分析

第一处变化

虽然转置了,但本质上还是要打印二维数组,所以仍然需要一个循环嵌套控制行和列的输出。我们可以重复使用i和j进行转置后的输出。

为了达到我们要的效果,第一步我们要做的就是对i和j下手,改造成下方这个样子:

for (i = 0; i < 5; i++)
{
	for (j = 0; j < 3; j++)
	{
		//这里先不管
	}
	printf("\n");

 为什么要改成这样呢?在理解这一点之前我们必须先明白打印的本质。通俗点讲,难道因为arr是二维数组打印就自动变成这种行和列的形式了吗?并不是的。二维数组在内存中是怎么存放的呢?其实和一维数组一样是连续存放的:

可不要理解成二维数组在内存中的存放就像打印二维数组那样,是以行和列的方式的啊! 

所以,真正决定二维数组打印成行和列的形式的其实是变量i和j,学过循环和数组内容的我们知道,i控制打印几行,j控制打印几列,for(i=0;i<3;i++)决定了打印出来是3行,for(j=0;j<5;j++)决定了打印出来是5列,当然还要在每一行内容打印完记得换行才能达到我们要的效果。

那么你可能会问,既然都是连续存放的,那二维数组和一维数组比有什么不同的?或者我们就说,arr[3][5]和arr[15]都是连续存放15个元素,有什么区别吗?其实,你可以把二维数组理解为一个存放了三个一维数组的数组,它的每个元素是一个一维数组,也就是它的每一行。正如我们上面在初始化二维数组arr时赋值内容里写的{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15},它们是三个一维数组,也是arr的每一行。

有点扯远了,我们不细讲二维数组的内容,现在我们回到这个问题来。

现在我们要转置,我们稍加观察可以发现,我们原本是要打印3行5列,现在要打印5行3列,所以根据上面说的,如果继续用i控制行输出,j控制列的输出,我们需要for(i=0;i<5;i++),for(j=0;j<3;j++)。

第二处变化

 到这一步为止,如果以为已经搞定了,运行时就会出现问题:

for (i = 0; i < 5; i++)
{
	for (j = 0; j < 3; j++)
	{
		printf("%3d ", arr[i][j]);
	}
	printf("\n");
}

return 0;

现在这个代码在vs2022中运行会变成这样: 

     

这是个什么情况呢?我们调试看看情况:

(这一部分如果没学过调试可以先跳过不看,影响不大)

右侧监视窗口i==1,j==0(结合左侧指向的位置、打印出的效果)可以看到我们现在是刚打印完arr[1][0],再看监视窗口的arr[1][0]是6,相当于打印完3我们就没能打印4、5,再结合前一张图,可以知道在每行打印完丢失两个数据后,最后我们又打印出了一些奇怪的数据。

我们看看vs是怎么提示的:

 

这是什么意思?我们arr[3][5]前一个括号内的取值在0~2,而我们循环的i却是for(i=0;i<5;i++),这已经超出了有效的范围,所以打印不出来有效的值。同样的,因为arr[3][5]后一个括号内的取值在0~4,我们写的却是for(j=0;j<3;j++),所以在行数越界之前,每打印一行都会丢失两个数据,在下一行开始打印的时候跳过了两个数据。

其实,我们应该将代码改成这样:

for (i = 0; i < 5; i++)
{
	for (j = 0; j < 3; j++)
	{
		printf("%3d ", arr[j][i]);//这里要写成arr[j][i]
	}
	printf("\n");
}

这段代码的运行效果:

我们分析一下为什么要写成arr[j][i],这一步较难理解。

本质上,我们已经知道i和j的范围决定了我们打印出来的行数、列数,而具体每一个位置上要打印什么数,则是由arr[][]括号里的下标决定的。

我们回到arr数组中看我们要打印第一行的1、6、11是哪几个元素:分别为arr[0][0]、arr[1][0]、arr[2][0]。我们可以发现对于同一行来说,变化的是前一个下标,固定的是后一个下标。

for (i = 0; i < 5; i++)
{
	for (j = 0; j < 3; j++)
	{
		printf("%3d ", arr[j][i]);//内循环,j每次变化的时候,
                                  //变化的是前一个下标,固定的是后一个下标。
                                  
	}
	printf("\n");
}

 是不是正如注释中说的那样?

同时,另一个角度来看,for(j=0;j<3;j++),j最大取值为2,而我们的数组arr[3][5]的前一个下标最大取值就为2,对于i来说也一样,这说明了没有越界的情况发生,打印的都是数组内的有效数据。

怎么样?现在应该能理解为什么要写成arr[j][i]了吧。

小结

总之在矩阵转置的时候我们要从两点入手,第一点我们要改变输出的行数和列数,第二点我们要改变打印的值的写法。而在解决第二点的时候,抓住变与不变的,以及注意数组越界和丢失需要的数据。

希望大家发现文章错误的地方能向我反馈,共同进步!

  • 34
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值