逝者如斯夫,不舍昼夜。
译文:逝去的时光如同这奔流不息的河水,昼夜不停地流向远方。
一、前言
在数据结构小学期实践中,因项目进度还没赶完,遂只好选了个特别特别水的题目(而且还是上个学期实验课中做过的...),做出来也没啥感觉,实际代码二百行,硬是被我凑出了五百行,有种郁闷之感现在。代码解析注释里有,若有不足之处,欢迎指出并相互交流。后续还会发个十字链表,以供学习用。
二、代码演示
设计思路:
- 首先定义一个三元组,包括元素在矩阵中的位置信息i,j,元素值e,然后定义矩阵结构,矩阵由三元组表data[MAX_SIZE+1],行数mu,列数tu组成;
- 矩阵相乘:传入一个参数用来返回生成的矩阵。先对两个矩阵是否能相乘进行判断,再使用四个变量以减少算法复杂度,index1是矩阵M的遍历器,index2是辅助更新M矩阵元素遍历起点,index3是矩阵M的遍历器,index4是辅助更新N矩阵元素遍历起点。
- 矩阵的逆置:重新创建一个矩阵,使用cpot[]数组用于记录列中第一个非零元素位置,辅助元素正确复制到转置矩阵T的相应位置;
- 矩阵的加减法函数为addMatrix(Matrix &M, Matrix &N, Matrix &O, int z);传入要加减的两个函数,并通过参数Matrix &O来返回运算得到的矩阵,通过控制参数z的值来控制加减法。首先对两个矩阵进行一次行优先排序,矩阵相加具体操作分五种情况,第一种,同行同列的点直接相加;第二种,再分为两点,第一,在同一行中,M中元素所在列小于在N中元素,第二,M中元素所在行比N中元素所在行小,此时直接将M中的这个元素直接加入进去;第三种,在同一行中,M中元素所在列大于N中元素,或者M中元素所在行比N中元素所在行大,则直接将N中这个元素加入到新矩阵中;在前三种情况发生完后,将另一个矩阵剩余的元素直接加入到新数组中,即为剩下的两种情况。
- 然后进行矩阵初始化操作,创建一个矩阵,并由控制台输入矩阵的行数和列数,元素个数,以及各个元素信息。并且创建出来的或者因矩阵操作产生的新矩阵将被存放入矩阵数组matrix[20]中;
- 退出程序:调用<windows.h>头文件,通过Sleep(),fflush(stdout)来控制程序退出事件
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<iomanip>
#include<windows.h>
#include<malloc.h>
#define MAXSIZE 100
typedef struct{
int i,j,e;
} Triple;
typedef struct
{
Triple data[MAXSIZE+1];
int mu, nu, tu;
} Matrix;
Matrix matrix[20];
int matrixNum = 0;//默认是0
int init_Maxtrix(Matrix &M);
int create_Matrix(Matrix &M);
int display(Matrix &M);
void menu();
int SortByRow(Matrix &M);
int exits();
int addMatrix(Matrix &M, Matrix &N, Matrix &O, int z);
int need_row_sort(Matrix &M);
int Transpose_Maxtrix(Matrix M, Matrix &T);
int askSave(int choose);
void swap_data(Triple &x, Triple &y);
int need_line_sort(Matrix &M);
int multiply_Matrix(Matrix M, Matrix N, Matrix &O);
int SortByLine(Matrix &M);
int checkMatrixNum();
//测试数据:3 3 3 0 0 1 1 1 2 1 2 3
//3 3 3 0 1 1 2 1 1 1 2 2
// 3 2 4 0 0 3 0 1 1 1 1 2 2 0 1
//2 2 3 0 0 2 0 1 2 1 0 1
int main(){
Matrix M;
int choose, a, b = 0;
printf("首先请您创建一个初始矩阵.\n");//这是第0个矩阵,初始矩阵
create_Matrix(M);
while(TRUE){
menu();
scanf("%d", &choose);
switch(choose){
case 1:
create_Matrix(matrix[matrixNum]);//debug-04,这里写成create_Matrix(matrix[matrixNum]);就会导致连续创建两个一模一样的矩阵
break;
case 2:
Matrix O1,H1;
if(checkMatrixNum()){
create_Matrix(H1);
break;
}
printf("请选择您要相加的两个矩阵,目前矩阵数量为%d(从0开始输入,比如2个矩阵,只有0 1):",matrixNum);
scanf("%d%d", &a, &b);
if(a<0 || a>=matrixNum || b<0 || b>=matrixNum){
int c = (a < 0 || a >= matrixNum) ? a : b;
printf("第%d个矩阵不存在\n", c);
break;
}
addMatrix(matrix[a], matrix[b], O1,1);
display(O1);
if(askSave(choose)){
matrix[matrixNum++] = O1;
}
break;
case 3:
Matrix O2,H2;
if(checkMatrixNum()){
create_Matrix(H2);
break;
}
printf("请选择您要相减的两个矩阵,当前矩阵数量为%d(从0开始输入,比如2个矩阵,只有0 1):",matrixNum);
scanf("%d%d", &a, &b);
if(a<0 || a>=matrixNum || b<0 || b>=matrixNum){
int c = (a < 0 || a >= matrixNum) ? a : b;
printf("第%d个矩阵不存在\n", c);
break;
}
addMatrix(matrix[a], matrix[b], O2,-1);
display(O2);
if(askSave(choose)){
matrix[matrixNum++] = O2;
}
break;
case 4:
Matrix O3,H3;
if(checkMatrixNum()){
create_Matrix(H3);
break;
}
printf("请输入你想要相乘的两个矩阵!当前矩阵数量为%d(从0开始输入,比如2个矩阵,只有0 1):",matrixNum);
scanf("%d%d", &a, &b);
if(a<0 || a>=matrixNum || b<0 || b>=matrixNum){
int c = (a < 0 || a >= matrixNum) ? a : b;
printf("第%d个矩阵不存在\n", c);
break;
}
multiply_Matrix(matrix[a], matrix[b], O3);
printf("相乘后的矩阵为:\n");
display(O3);
if(askSave(choose)){
matrix[matrixNum++] = O3;
}
break;
case 5:
Matrix T;
printf("请输入你想要转置的矩阵:\n");
scanf("%d", &a);
if(a>=0 && a<matrixNum){
Transpose_Maxtrix(matrix[a], T);
printf("转置后的矩阵为:\n");
display(T);
if(askSave(choose)){
matrix[a] = T;
}
}else{
printf("这个矩阵不存在哦!\n");
}
break;
case 6:
//查看一个矩阵
printf("请输入您想要查看的矩阵,当前矩阵数量为%d(从0开始输入,比如2个矩阵,只有0 1):\n", matrixNum);
scanf("%d", &a);
if(a>=matrixNum){
printf("矩阵数量没那么多,返回重新选择!");
break;
}
display(matrix[a]);
break;
case 7:
exits();
}
}
system("pause");
return 0;
}
void menu(){
printf("请输入对应矩阵操作的序号:\n");
printf("1:创建一个新的矩阵;\n2:输出两个矩阵相加的结果;\n3:输入两个矩阵相减的结果;\n4:输出两个矩阵相乘的结果;\n5:输出一个矩阵的转置矩阵;\n6:查看一个矩阵\n7:退出程序.\n");
}
int checkMatrixNum(){
int check = 0;
if(matrixNum < 2){
printf("矩阵数量不足两个,请再创建一个!\n");
check = 1;
}
return check;
}
int init_Maxtrix(Matrix &M){
int i, j, e, num = 1;
printf("请输入三元表的行,列,值(三个为一组,用空格隔开):\n");
while(num<=M.tu){
scanf("%d%d%d", &i, &j, &e);
if(i>=M.mu || j>= M.nu){
printf("输入元组的行或列大于矩阵的行或列,请重新输入!\n");
num--;
}else{
M.data[num].i = i;
M.data[num].j = j;
M.data[num].e = e;
}
num++;
}
return 1;
}
int create_Matrix(Matrix &M){
int mu, nu, tu = 0;
printf("请输入创建的稀疏矩阵的行数、列数、非0元素个数:\n");
scanf("%d%d%d", &mu, &nu, &tu);
if(mu <=0 || nu <= 0 || tu <=0){
printf("输入非法数据!\n");
return 0;
}
M.mu = mu;
M.nu = nu;
M.tu = tu;
init_Maxtrix(M);
printf("稀疏矩阵创建成功,添加为第%d个矩阵(确信)\n",matrixNum+1);
display(M);
matrix[matrixNum++] = M;
return 1;
}
int display(Matrix &M){
int i=0,j = 0;
int k = 1;
SortByRow(M);//debug-01,果然还是需要排序,不过应该在创建之后立刻排序更好
for (i = 0; i < M.mu;i++){
printf("|");
for (j = 0; j < M.nu;j++){
if(M.data[k].i == i && M.data[k].j==j){
printf("%2d", M.data[k].e);
k++;
}
else{
printf(" 0");
}
}
printf(" |\n");
}
printf("\n");
return 1;
}
int addMatrix(Matrix &M,Matrix &N,Matrix &O,int z){
int e = 0, t = 1, k = 1, x = 1;
if(M.mu != N.mu || M.nu != N.nu){
printf("两个矩阵的行数和列数都不相等,你没学过线性代数吗,这不能相加!\n");
return 0;
}
O.mu = M.mu;
O.nu = M.nu;
O.tu = 0;
//创建之后有一次display(),在display()方法中已经进行过一次排序
// SortByRow(M);
// SortByRow(N);
while(t<=M.tu&&k<=N.tu){
//第一种情况,同行同列的点直接相加
if((M.data[t].i == N.data[k].i) && (M.data[t].j == N.data[k].j)){
e = M.data[t].e + z * N.data[k].e;
//即使和为0,同样作为一个新的元素
O.data[x].i = M.data[t].i;
O.data[x].j = M.data[t].j;
O.data[x++].e = e;
t++;
k++;
}
// 第二种情况,分两点,1.在同一行中,M中的元素在N之前;2.M中元素所在行比N中元素所在行小;这两点都是将M的元素直接加入进去。
else if(((M.data[t].i==N.data[k].i)&&(M.data[t].j<N.data[k].j))||(M.data[t].i<N.data[k].i)){
O.data[x].i = M.data[t].i;
O.data[x].j = M.data[t].j;
O.data[x++].e = M.data[t].e;
t++;
}
// 第三种情况,分两点,1.在同一行中,M中的元素在N之后,2.M中元素所在行比N中元素所在行大;
else if(((M.data[t].i==N.data[k].i)&&(M.data[t].j>N.data[k].j))||(M.data[t].i>N.data[k].i))
{
O.data[x].i = N.data[k].i;
O.data[x].j = N.data[k].j;
O.data[x++].e = z * N.data[k].e;
k++;
}
else{
printf("Error...is...in...add_Maxtrix...\n");
}
}
//当一个矩阵的元素已经全部添加完后,来将另一个矩阵剩余的元素直接加入到新数组
while(t<=M.tu){
O.data[x].i = M.data[t].i;
O.data[x].j = M.data[t].j;
O.data[x++].e = 0 + M.data[t].e;
t++;
}
while(k<=N.tu){
O.data[x].i = N.data[k].i;
O.data[x].j = N.data[k].j;
O.data[x++].e = 0 + z * N.data[k].e;
k++;
}
O.tu = x-1;
return 1;
}
//判断是否要进行"行排序",从小到大排序
/*
存在不足之处,因为如果我只有一个顺序不一致,那么就要重新排序
*/
int need_row_sort(Matrix &M){
int judge = 1;
for (int i = 1; i <= M.tu;i++){
if(M.data[i].i > M.data[i+1].i){
judge = 0;
}
}
return judge;
}
//行优先冒泡排序
int SortByRow(Matrix &M){
//debug-02:如果需要行排序,函数返回的是0,即不会进入这个if语句
if(need_row_sort(M)){
return 0;
}
if(M.tu){
for (int i = 1; i <= M.tu;i++){
for (int j = i+1; j <= M.tu;j++)
{
// debug-01:j = i+1; not j=1;
//第一,行比较;第二,同行比较列,将小列放在前面
if(M.data[i].i>M.data[j].i || (M.data[i].i == M.data[j].i && M.data[i].j > M.data[j].j)){
swap_data(M.data[i], M.data[j]);
}
}
}
}//if
return 1;
}
//判断是否需要列排序
int need_line_sort(Matrix &M){
int judge = 1;
for (int j = 1; j <= M.tu;j++){
if(M.data[j].j > M.data[j+1].j){
judge = 0;
}
}
return judge;
}
//列优先冒泡排序
int SortByLine(Matrix &M)
{
if(need_line_sort(M)){
return 0;
}
if(M.tu)
{
for(int i=1; i<=M.tu; i++)
{
for(int j=i+1; j<=M.tu; j++)
{
//列小放前面,列同把行小放前面
if((M.data[i].j>M.data[j].j||(M.data[i].j==M.data[j].j)&&M.data[i].i>M.data[j].i))
{
swap_data(M.data[i],M.data[j]);
}
}
}
}
return 1;
}
//退出程序稍加处理
int exits(){
int operate;
printf("你确定要退出程序吗?(输入1退出0返回)\n");
scanf("%d", &operate);
if(operate == 1){
printf("程序将在3秒后退出!\n");
fflush(stdout);
Sleep(3000);
exit(0);
}
return 1;
}
//矩阵的逆置
int Transpose_Maxtrix(Matrix M,Matrix &T){
int col, t, p, q;
int cpot[50], num[50];
T.mu = M.nu;
T.nu = M.mu;
T.tu = M.tu;
if(T.tu){
for (col = 1; col <= M.mu;++col){
num[col] = 0;//num[col]表示矩阵M中第col列非零元的个数
}
for (t = 1; t <= M.tu;++t){
++num[M.data[t].j];
cpot[1] = 1;
//求第col列中第一个非零元在三元组表b.data中的序号
for (col = 2; col <= M.nu;++col){
cpot[col] = cpot[col - 1] + num[col - 1];
}
for (p = 1; p <= M.tu;p++){
col = M.data[p].j;
q = cpot[col];
T.data[q].i = M.data[p].j;
T.data[q].j = M.data[p].i;
T.data[q].e = M.data[p].e;
++cpot[col];
}//for
}//if
}
return 1;
}
int askSave(int choose){
int choice = 0;
switch(choose){
case 2:case 3:
printf("请问你要保存这个相加/减之后的矩阵吗?(输入1肯定,0否定):\n");
scanf("%d",&choice);
break;
case 4:
printf("请问你想要保存这个相乘之后的矩阵吗?(输入1肯定,0否定):\n");
scanf("%d",&choice);
break;
case 5:
printf("请问你想要用转置后的矩阵替代原矩阵吗?(输入1肯定,0否定):\n");
scanf("%d", &choice);
break;
default:
break;
}
return choice;
}
void swap_data(Triple &x, Triple &y){
Triple temp = x;
x = y;
y = temp;
}
//矩阵的乘法
int multiply_Matrix(Matrix M,Matrix N,Matrix &O){
int i = 0, j = 0, k = 1; // i,j,k分别表示M,N,O三个矩阵的非零元素序号
SortByLine(N);//排序功能没问题
while(M.nu != N.mu){
printf("由线性代数知,左边的列数和右边的行数需要相等,这两个都不相等这咋乘,给你打回去了!\n");
if(M.mu == N.nu){
printf("矩阵相乘顺序搞反了,左边行数等于右边列数!回去重新写过顺序!\n");
}
return 0;
}
if(!M.tu || !N.tu){
printf("有一个矩阵是零矩阵!相乘得到的矩阵为零矩阵!\n");
return 0;
}
//"左行右列"
O.mu = M.mu;
O.nu = N.nu;
O.tu = 0;
int index1 = 1, index2 = 1;
//index1是矩阵M的遍历器;
//index2辅助更新M矩阵元素遍历起点,其值为矩阵M中第一个行数等于当前i的元素下标的元素序号
for (i = 0; i < O.mu; i++)
{
int index3 = 1, index4 = 1;
// index3是矩阵N的遍历器;
// index4辅助更新N矩阵元素遍历起点,其值为矩阵N中第一个列数等于当前j的元素下标的元素序号
for (j = 0; j < O.nu; j++)//
{
int temp=0;//O在(i行,j列)处的值
for(index1=index2;index1<=M.tu && M.data[index1].i==i; index1++)//debug-03:i起点有问题,应该为如果i从1开始,那么0就匹配不到了//在index1不越界且M的第index1个元素在第i行的前提下遍历M的元素
{
for(index3=index4; index3<=N.tu&&N.data[index3].j==j; index3++)
//在index3不越界且N的第index3个元素在第j列的前提下遍历N的元素
{
if(M.data[index1].j==N.data[index3].i)//两个元素位置匹配相乘即可
{
temp=temp+M.data[index1].e*N.data[index3].e;//累加到temp上
index4++;//记录一下元素
}
//不匹配的话,则继续看下一个元素
}//for-04
if(index1 == M.tu || M.data[index1+1].i != i)
//当矩阵M的所有在第i行的元素遍历完成一遍后(index1==M.tu针对矩阵M的最后一行)
{
//当这一列所有元素遍历完成后,就直接交换到下一个元素,在for语句中会过掉第一个,直接从后一个元素开始
// if(N.data[index3+1].j != j ){ //把这行删掉完美实现!!!
// index3 = index4;//因为之前的元素可能还会选中
index4 = index3;
// }
//此时N.data[index3].j=j+1,将该元素下标值复制到index4中,下次遍历下一列时无需从头开始
}
}//for-03
if (temp)
{
O.data[k].i = i;
O.data[k].j = j;
O.data[k].e = temp;
k++;
}
//当矩阵O的第i行求值完成后
if(j == O.nu-1)
{
index2 = index1;
// index1 = index2;
//此时M.data[index1].i=i+1,将该下标值复制到index2中,下次遍历下一行时无需从头开始
}
} // for-02
O.tu = k-1;
} // for-01
return 1;
}
三、演示截图
1、稀疏矩阵的加法
测试数据:1(选择第一个操作序号)
3 10 4(这里表示三行10列,4个元素)
2 4 1 0 6 9 1 3 3 1 1 1(三个为一组)
还有一组:10 3 3 1 1 2 3 2 3 8 1 5(先把前面这个转置了,才能相加)
2、稀疏矩阵的逆置
3、稀疏矩阵的乘法
再提供一组测试数据:30 30 9 25 10 2 28 9 19 26 22 10 10 2 7 1 1 2 3 2 3 8 1 5 0 6 9 1 3 3。
四、结尾语
程序是可以运行的,有不足之处请指出,欢迎共同交流,共同进步!