数据结构小学期实践——三元组表稀疏矩阵的基本操作(加、减、乘、逆置)

逝者如斯夫,不舍昼夜。

译文:逝去的时光如同这奔流不息的河水,昼夜不停地流向远方。


一、前言

在数据结构小学期实践中,因项目进度还没赶完,遂只好选了个特别特别水的题目(而且还是上个学期实验课中做过的...),做出来也没啥感觉,实际代码二百行,硬是被我凑出了五百行,有种郁闷之感现在。代码解析注释里有,若有不足之处,欢迎指出并相互交流。后续还会发个十字链表,以供学习用。

二、代码演示

设计思路:

  1. 首先定义一个三元组,包括元素在矩阵中的位置信息i,j,元素值e,然后定义矩阵结构,矩阵由三元组表data[MAX_SIZE+1],行数mu,列数tu组成;
  2. 矩阵相乘:传入一个参数用来返回生成的矩阵。先对两个矩阵是否能相乘进行判断,再使用四个变量以减少算法复杂度,index1是矩阵M的遍历器,index2是辅助更新M矩阵元素遍历起点,index3是矩阵M的遍历器,index4是辅助更新N矩阵元素遍历起点。
  3. 矩阵的逆置:重新创建一个矩阵,使用cpot[]数组用于记录列中第一个非零元素位置,辅助元素正确复制到转置矩阵T的相应位置;
  4. 矩阵的加减法函数为addMatrix(Matrix &M, Matrix &N, Matrix &O, int z);传入要加减的两个函数,并通过参数Matrix &O来返回运算得到的矩阵,通过控制参数z的值来控制加减法。首先对两个矩阵进行一次行优先排序,矩阵相加具体操作分五种情况,第一种,同行同列的点直接相加;第二种,再分为两点,第一,在同一行中,M中元素所在列小于在N中元素,第二,M中元素所在行比N中元素所在行小,此时直接将M中的这个元素直接加入进去;第三种,在同一行中,M中元素所在列大于N中元素,或者M中元素所在行比N中元素所在行大,则直接将N中这个元素加入到新矩阵中;在前三种情况发生完后,将另一个矩阵剩余的元素直接加入到新数组中,即为剩下的两种情况。
  5. 然后进行矩阵初始化操作,创建一个矩阵,并由控制台输入矩阵的行数和列数,元素个数,以及各个元素信息。并且创建出来的或者因矩阵操作产生的新矩阵将被存放入矩阵数组matrix[20]中;
  6. 退出程序:调用<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。

四、结尾语

程序是可以运行的,有不足之处请指出,欢迎共同交流,共同进步!

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值