C/C++矩阵综合转化的处理思路

矩阵常见的转换方式有旋转和倒置两种(本人还没学到线性代数~~doge).

1.矩阵旋转

矩阵的创建需要对二维数组的行列,元素进行赋初始值(这里假定 i 为行,j 为列).

建立后的矩阵旋转的方式有顺时针和逆时针两种,无论哪种,都需要对行,列进行数量关系转化.

1.1顺时针旋转

比如输入

i = 3    j = 5

c[3][5] = { { 4,7,9,1,21 } , { 11,14,2,3,5 } , { 15,29,11,45,14 } }

就形成了这样一个矩阵

       1列

        (0)

        2列

        ( 1 )

        3列

    ( j - 3 )

        4列

      ( j - 2 )

    5列

( j - 1 )

1行(0)

         4        7          9         1       21
2行( i - 2 )       11       14         2         3         5
3行( i - 1 )       15       29         11       45       14

若要完成顺时针旋转,形成以下矩阵

            0          i - 2         i - 1
            0           15           11           4
           1           29           14           7
         j - 3           11           2           9
         j - 2           45           3           1
         j - 1           14           5           21

不妨对 a - 1 行,b - 1 列的元素坐标记为( a , b )( 起始行列均为0 ).

不难发现,对于矩阵中的任意一个元素,无论是代数特征还是几何特点(实际上是脑补~).

其坐标( a , b )都转化为( b ,( i - 1 ) - a ),

如原( i - 1 , j - 2 )的45坐标转化为( j - 2 ,i  - 1 - ( i - 1 ) )   即( j - 2 , 0 ).

于是你十分自信地写下了如下代码

for (a = 0; a <= i - 1; a++)         
{                                    
	for (b = 0; b <= j - 1; b++)
	{
		c[b][i - 1 - a] = c[a][b];
	}
}

结果......咦

为什么我的部分数据混乱了?

原因:若只对原数组元素间进行赋值,会使部分元素的值丢失!

图例如下

因此,我们需要一个新的二维数组来接收原数组的值的转换

for (a = 0; a <= i - 1; a++)         
{                                    
	for (b = 0; b <= j - 1; b++)
	{
		d[b][i - 1 - a] = c[a][b];  //新数组d[][]
	}
}

以下就是该顺时针旋转的函数

void clockwise(int i, int j)
{
	int a, b;
	int c[N][N], d[N][N];          //使用新数组d是为了防止原数组在赋值过程中丢失数据
	printf("请输入矩阵的行 列:");
	scanf("%d%d", &i, &j);
	for (a = 0; a <= i - 1; a++)
	{
		printf("请输入第%d行的数:", a+1);
		for (b = 0; b <= j - 1; b++)
		{
			scanf("%d", &c[a][b]);
		}
	}
	for (a = 0; a <= i - 1; a++)         //旋转后新矩阵的列为原来矩阵的纵长-行
	{                                    //新矩阵的行为原来矩阵的列
		for (b = 0; b <= j - 1; b++)
		{
			d[b][i - 1 - a] = c[a][b];
		}
	}
	printf("矩阵顺时针旋转后:\n");
	for (a = 0; a <= j - 1; a++)
	{
		for (b = 0; b <= i - 1; b++)
		{
			printf("%d ", d[a][b]);
		}
		printf("\n");
	}
}

1.2逆时针旋转

逆时针旋转和顺时针旋转大同小异,此处略述

这次使用占用内存较少的输入(其实是本人懒了~)

i = 3    j = 2

e[3][2] = { { 4 , 21 } , { 14 , 5 } , { 29 , 45 } }

矩阵如下 

           0        j - 1 (1)
           0            4            21
           1           14             5
        i - 1 (2)           29             45

逆时针旋转后

         0        1   i - 1 (2)
        0        21        5        45
   j - 1 (1)        4        14        29

依上顺时针方法,不难想到其中一元素( c , d )坐标变换:

( c , d ) ————>( j - 1 - d , c )

考虑到数据丢失,同理也需要一个新的二维数组接收.

该部分代码如下

void anticlockwise(int i, int j)
{
	int c, d;
	int e[N][N], f[N][N];                    //使用新数组f是为了防止原数组在赋值过程中丢失数据
	printf("请输入矩阵的行 列:"); 
	scanf("%d%d", &i, &j);
	for (c = 0; c <= i - 1; c++)
	{
		printf("请输入第%d行的数:", c+1);
		for (d = 0; d <= j - 1; d++)
		{
			scanf("%d", &e[c][d]);
		}
	}
	for (c = 0; c <= i - 1; c++)             //旋转后新矩阵的列为原来矩阵的行
	{                                        //新矩阵的行为原来矩阵的横长-列
		for (d = 0; d <= j - 1; d++)
		{
			f[j - 1 - d][c] = e[c][d];
		}
	}
	printf("矩阵逆时针旋转后:\n");
	for (c = 0; c <= j - 1; c++)
	{
		for (d = 0; d <= i - 1; d++)
		{
			printf("%d ", f[c][d]);
		}
		printf("\n");
	}
}

2.矩阵倒置

矩阵的倒置是将矩阵每个元素的行 列交换

如输入

i = 3    j = 5

c[3][5] = { { 4,7,9,1,21 } , { 11,14,2,3,5 } , { 15,29,11,45,14 } }

倒置效果如下 

按照常规思路,

倒置,将每一个元素的行 列进行交换,

但是这样必然会出现重复交换,导致交换失败的情况,

如若 g < h ,在嵌套的两个for循环中,

( g , h ) 和( h , g )交换后( h , g )又会和 ( g , h )交换,产生重复。

那这一步该如何实现呢?

这里我使用了对称性,如下

中轴蓝线所覆的元素在倒置后不改变,其两侧的元素进行交换 ,该方法只需将蓝线一侧的元素与另一侧交换一次(不能忽略空的橙色区域),不会产生重复和错误.

但这里需要分类讨论

2.1

1.行 < 列 时(就是上述情况)

有四种倒置方法

2.1.1 由蓝线右上方与左下方倒置

该类又分为横向和纵向两种

2.1.1.1横向

从第1行第2列开始,到第1行第 j 列,

再从第2行第3列开始,到第2行第 j 列,

......

......

最后从第 i 行第 i + 1列开始,到第 i 行第 j 列,依次对称转换

代码如下(g是行的参数,h是列的参数)

for (g = 1; g <= i; g++)                   
{
	for (h = g + 1; h <= j; h++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
2.1.1.2纵向

在该方法中,每一列的倒置过程会在 该列数-1 列 和 i 列的较小者处结束,取 s = min{ g - 1 ,i }(以下均用s表示)

从第2列第1行直接结束 ,再

从第3列第1行开始,到第3列第 s 列,

......

......

最后从第 j 列第1行开始,到第 j 列第 s 列,依次对称转换(参考上图的几何意义)

for (h = 1; h <= j; h++)
{
	if (h - 1 < i)
		s = h - 1;
	else
		s = i;
	for (g = 1; g <= s; g++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
 2.1.2 由蓝线左下方与右上方倒置
2.1.2.1纵向

上图橙色空区域不能忽略,仍可进行交换

从第1列第2行开始,到第1列第 j 行结束,

从第2列第3行开始,到第2列第 j 行结束,

......

......

最后从第 i 列第3行开始,到第 i 列第 j 行结束,依次对称转换.

for (h = 1; h <= i; h++)
{
	for (g = h + 1; g <= j; g++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
2.1.2.2横向

参考第一类的纵向方法,在该方法中,每一行的倒置过程会在 该行数-1 列 和 i 列的较小者处结束,取 s = min{ g - 1 ,i }(以下均用s表示)

从第2行第1列开始,到第2行第 s 列,

从第3行第1列开始,到第3行第 s 列,

......

......

最后从第 j 行第1列开始,到第 j 行第 s 列,依次对称转换(参考上图的几何意义)

代码如下

for (g = 2; g <= j; g++)
{
    if (g - 1 < i)
	    s = g - 1;
    else
	    s = i;
	for (h = 1; h <= g; h++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}

2.2

1.行 > 列 时(就是上述情况)

同样有两种倒置方法

2.2.1由红线左下方与右上方倒置
2.2.1.1纵向

从第1列第2行开始,到第1列第 i 行,

从第2列第3行开始,到第2列第 i 行,

......

......

最后从第 j 列第 j + 1 行,到第 j 列第 i 行结束.

for (h = 1; h <= j; h++)
{
	for (g = h + 1; g <= i; g++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
2.2.1.2横向

(s = min{ g - 1 , j ))

从第2行第1列开始,到第2行第 s 列,

从第3行第1列开始,到第3行第 s 列,

......

......

最后从第 i 行第1列开始,到第 i 行第 s 列,依次对称转换(参考上图的几何意义)

代码如下

for (g = 2; g <= i; g++)
{
	if (g - 1 < j)
		s = g - 1;
	else
		s = j;
	for (h = 1; h <= s; h++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
2.2.2.1由红线右上方与左下方倒置
2.2.2.1横向

从第1行第2列开始,到第1行第 i 列,

从第2行第3列开始,到第2行第 i 列,

......

......

最后从第 j 行第 j + 1 列开始,到第 j 行第 i 列,依次对称转换.

for(g = 1; g <= j; g++)
{
	for(h = g+1; h <= i; h++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}
2.2.2.2纵向

s = min{ g - 1 , j }

从第2列第1行开始直接结束,

从第3列第1行开始,到第3列第 s 行结束,

......

......

最后从第 i 列第1行开始,到第 3 列第 s 行结束,依次对称转换.

for(h = 2; h <= i; h++)
{
	if(h - 1 < j)
	    s = h - 1;
	else
	    s = j;
	for(g = 1; g <= s; g++)
	{
		n = l[g][h];
		l[g][h] = l[h][g];
		l[h][g] = n;
	}
}

2.3总结

通过总结,我们不难发现,无论 i , j 谁更大,无论是中轴左下方还是右上方,无论是横向还是纵向,其核心思想都是将轴线一侧的元素不多不少,正确地进行对称交换,只不过方法选取有所不同

而且我们发现2.1.1.1与2.2.2.1   2.1.1.2与2.2.2.2   2.1.2.1与2.2.1.1   2.1.2.2与2.2.1.2所使用的方式和代码结构是相似的,在每组中通过几何特点和归纳——

得到以下总结后代码

void inversion(int i, int j)
{
	int g, h, s;
	int l[10][10] = { 0 }, n;
	int v;
	printf("请输入要选择的方法(1,2,3,4)\n");
	scanf("%d", &v);
	printf("请输入矩阵的行 列:");
	scanf("%d%d", &i, &j);
	for (g = 1; g <= i; g++)
	{
		printf("请输入第%d行的数:", g);
		for (h = 1; h <= j; h++)
			scanf("%d", &l[g][h]);
	}
	switch (v)
	{
	case 1:
		if (i < j)
		{
			for (g = 1; g <= i; g++)//i < j的方法一 
			{
				for (h = g + 1; h <= j; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
	    }
		else
		{
			for (g = 1; g <= j; g++)//i > j方法三
			{
				for (h = g + 1; h <= i; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 2:
		if (i < j)
		{
			for (g = 2; g <= j; g++)//i < j的方法二
			{
				if (g - 1 < i)
					s = g - 1;
				else
					s = i;
				for (h = 1; h <= g; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (h = 2; h <= i; h++)//i > j方法四
			{
				if (h - 1 < j)
					s = h - 1;
				else
					s = j;
				for (g = 1; g <= s; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 3:
		if (i < j)
		{
			for (h = 1; h <= j; h++)//i < j的方法三
			{
				if (h - 1 < i)
					s = h - 1;
				else
					s = i;
				for (g = 1; g <= s; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (h = 1; h <= j; h++)//i > j的方法一
			{
				for (g = h + 1; g <= i; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 4:
		if (i < j)
		{
			for (h = 1; h <= i; h++)//i < j的方法四
			{
				for (g = h + 1; g <= j; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (g = 2; g <= i; g++)//i > j方法二
			{
				if (g - 1 < j)
					s = g - 1;
				else
					s = j;
				for (h = 1; h <= s; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	default:
		printf("请好好输入选择的方法:");
		scanf("%d", &v);
		break;
	}
	printf("矩阵倒置后:\n");
	for (g = 1; g <= j; g++)
	{
		for (h = 1; h <= i; h++)
			printf("%d ", l[g][h]);
		printf("\n");
	}
}

矩阵转化的所有代码如下,希望对你有帮助~

#define _CRT_SECURE_NO_WARNINGS
#define N 32
#include<stdio.h>
//____________________________________________________________
void clockwise(int i, int j)
{
	int a, b;
	int c[N][N], d[N][N];          //使用新数组d是为了防止原数组在赋值过程中丢失数据
	printf("请输入矩阵的行 列:");
	scanf("%d%d", &i, &j);
	for (a = 0; a <= i - 1; a++)
	{
		printf("请输入第%d行的数:", a + 1);
		for (b = 0; b <= j - 1; b++)
		{
			scanf("%d", &c[a][b]);
		}
	}
	for (a = 0; a <= i - 1; a++)         //旋转后新矩阵的列为原来矩阵的纵长-行
	{                                    //新矩阵的行为原来矩阵的列
		for (b = 0; b <= j - 1; b++)
		{
			d[b][i - 1 - a] = c[a][b];
		}
	}
	printf("矩阵顺时针旋转后:\n");
	for (a = 0; a <= j - 1; a++)
	{
		for (b = 0; b <= i - 1; b++)
		{
			printf("%d ", d[a][b]);
		}
		printf("\n");
	}
}
//____________________________________________________________
void anticlockwise(int i, int j)
{
	int c, d;
	int e[N][N], f[N][N];                    //使用新数组f是为了防止原数组在赋值过程中丢失数据
	printf("请输入矩阵的行 列:");
	scanf("%d%d", &i, &j);
	for (c = 0; c <= i - 1; c++)
	{
		printf("请输入第%d行的数:", c + 1);
		for (d = 0; d <= j - 1; d++)
		{
			scanf("%d", &e[c][d]);
		}
	}
	for (c = 0; c <= i - 1; c++)             //旋转后新矩阵的列为原来矩阵的行
	{                                        //新矩阵的行为原来矩阵的横长-列
		for (d = 0; d <= j - 1; d++)
		{
			f[j - 1 - d][c] = e[c][d];
		}
	}
	printf("矩阵逆时针旋转后:\n");
	for (c = 0; c <= j - 1; c++)
	{
		for (d = 0; d <= i - 1; d++)
		{
			printf("%d ", f[c][d]);
		}
		printf("\n");
	}
}
//____________________________________________________________
void inversion(int i, int j)
{
	int g, h, s;
	int l[10][10] = { 0 }, n;
	int v;
	printf("请输入要选择的方法(1,2,3,4)\n");
	scanf("%d", &v);
	printf("请输入矩阵的行 列:");
	scanf("%d%d", &i, &j);
	for (g = 1; g <= i; g++)
	{
		printf("请输入第%d行的数:", g);
		for (h = 1; h <= j; h++)
			scanf("%d", &l[g][h]);
	}
	switch (v)
	{
	case 1:
		if (i < j)
		{
			for (g = 1; g <= i; g++)//i < j的方法一 
			{
				for (h = g + 1; h <= j; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
	    }
		else
		{
			for (g = 1; g <= j; g++)//i > j方法三
			{
				for (h = g + 1; h <= i; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 2:
		if (i < j)
		{
			for (g = 2; g <= j; g++)//i < j的方法二
			{
				if (g - 1 < i)
					s = g - 1;
				else
					s = i;
				for (h = 1; h <= g; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (h = 2; h <= i; h++)//i > j方法四
			{
				if (h - 1 < j)
					s = h - 1;
				else
					s = j;
				for (g = 1; g <= s; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 3:
		if (i < j)
		{
			for (h = 1; h <= j; h++)//i < j的方法三
			{
				if (h - 1 < i)
					s = h - 1;
				else
					s = i;
				for (g = 1; g <= s; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (h = 1; h <= j; h++)//i > j的方法一
			{
				for (g = h + 1; g <= i; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	case 4:
		if (i < j)
		{
			for (h = 1; h <= i; h++)//i < j的方法四
			{
				for (g = h + 1; g <= j; g++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
		else
		{
			for (g = 2; g <= i; g++)//i > j方法二
			{
				if (g - 1 < j)
					s = g - 1;
				else
					s = j;
				for (h = 1; h <= s; h++)
				{
					n = l[g][h];
					l[g][h] = l[h][g];
					l[h][g] = n;
				}
			}
		}
	default:
		printf("请好好输入选择的方法:");
		scanf("%d", &v);
		break;
	}
	printf("矩阵倒置后:\n");
	for (g = 1; g <= j; g++)
	{
		for (h = 1; h <= i; h++)
			printf("%d ", l[g][h]);
		printf("\n");
	}
}
//____________________________________________________________
int main()
{
	int i = 0, j = 0, a, b, m;
	printf("请选择矩阵的变换方式\n");
	printf("1.顺时针旋转   2.逆时针旋转   3.矩阵倒置   0.退出\n");
	printf("请选择:");
	while (EOF != scanf("%d", &m))
	{
		switch (m)
		{
		case 0:
			printf("已退出\n");
			return 0;
		case 1:
			clockwise(i, j);
			break;
		case 2:
			anticlockwise(i, j);
			break;
		case 3:
			inversion(i, j);
			break;
		default:
			printf("重新输入\n");
			break;
		}
		printf("请选择:");
	}
	return 0;
}

本人是大一的学生,第一次写博客,很多知识掌握得还不太好,如果上文有错误或需要改进的地方,欢迎大家指正,我也会慢慢进步哒~~

本人gitee账号:小奶狼 大柔情 (runrun-brother) - Gitee.com

原创不易,请勿未经允许转载~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值