【数据结构】|实验五:稀疏矩阵

数据结构:第七章数组与矩阵笔记

题目

注意

  1. 数据类型请使用int,本题中所有运算的结果均视作对int型自然溢出
  2. 各操作需在稀疏矩阵上进行,充分考虑数据的稀疏性, 不得直接或间接转换为二维数组形式计算 ,否则取消成绩

题目描述

  • 创建 稀疏矩阵类 (参照课本 MatrixTerm 三元组定义) ,采用行主顺序把稀疏矩阵非0元素映射到一维数组中,提供操作:两个稀疏矩阵相加、两个稀疏矩阵相乘、稀疏矩阵的转置、输出矩阵。
  • 键盘输入矩阵的行数、列数;并按行优先顺序输入矩阵的各元素值,建立矩阵;
  • 对建立的矩阵执行相加、相乘、转置的操作,输出操作的结果矩阵。

操作描述

  • 为方便操作描述,我们假设存在一个矩阵 P,下列各操作实际为对矩阵 P 的操作。
  • 重置矩阵 :
    1
    矩阵的行数n 矩阵的列数m
    [n行m列 表示矩阵中的所有元素]
    即重置矩阵 P 的尺寸为 n 行 m 列,且随后按行优先顺序输入矩阵 P 的各个元素。
  • 矩阵乘法:
    2
    矩阵的行数 矩阵的列数
    矩阵中非零元素个数t
    [t行 表示矩阵中非零元素]
    t 行非零元素已按行优先顺序给出,矩阵中非零元素的表示为 x y v,其中 x 表示行序号,y 表示列序号,v 表示非零元素值,行列序号从 1 开始。
    设输入的矩阵为 Q,若 PxQ 运算合法,则将 PxQ 的结果矩阵赋给 P,若不合法,则将 Q 赋给 P,同时输出 -1。
  • 矩阵加法:
    3
    矩阵的行数 矩阵的列数
    矩阵中非零元素个数t
    [t行 表示矩阵中非零元素]
    t 行非零元素已按行优先顺序给出,矩阵中非零元素的表示为 x y v,其中 x 表示行序号,y 表示列序号,v 表示非零元素值,行列序号从 1 开始。
    设输入的矩阵为 Q,若 P+Q 运算合法,则将 P+Q 的结果矩阵赋给 P,若不合法,则将 Q 赋给 P,同时输出 -1。
  • 输出操作:
    4
    设当前矩阵 P 的尺寸为 n 行 m 列,第一行输出矩阵 P 的行数和列数,随后 n 行按行优先顺序输出矩阵 P,每行 m 个数字,来表示当前的矩阵内容,每行数字之间用空格分隔。
  • 转置操作:
    5
    设当前矩阵 P 的尺寸为 n 行 m 列,将其转置为 m 行 n 列的矩阵,无需输出。

格式

  • 输入

第一行一个 w 代表操作个数,接下来若干行是各个操作,其中保证第一个操作一定为重置矩阵。

  • 输出

当执行操作 4 时,输出矩阵 P;当执行操作 2 或 3 时,若对应运算不合法,则输出 -1。

数据范围

思路

整体思路:

  • 定义一个矩阵三元组结构体,用于表示非零元素的行索引、列索引、值
  • 定义一个稀疏矩阵类,用来实现矩阵的各种操作,包括重置、相乘、相加、输出、转置
  • 定义主函数,按照要求进行选择操作

具体思路:

基本函数

  • 构造函数:
    • 初始化稀疏矩阵的行数、列数、非零元素个数,并为矩阵三元组线性表元素动态分配存储空间
  • 析构函数:释放构造函数中分配的内存空间
  • 重载运算符==:将一个稀疏矩阵赋给另一个稀疏矩阵
  • 输入函数input:
    • 实现将非零元素的行索引、列索引的值输入到当前稀疏矩阵中
    • 首先创建一个三元组对象,并将参数赋给相应的变量
    • 然后将这个三元组对象添加到稀疏矩阵的element数组中,逐步构建稀疏矩阵的非零元素

重置reset

  • 按行优先顺序,从标准输入中读取矩阵元素
  • 将非零元素以行优先顺序储存在类的成员变量一维数组中
  • 非零元素个数++

相乘

  • 首先进行判断相乘的合法性 m × n ∗ p × q m×n*p×q m×np×q):n=p?
    • 若不满足条件,输出-1,并返回;
  • 创建一个结果矩阵result,其行数、列数分别为当前矩阵的行数、输入矩阵的列数:sparseMatrix result(rows,Q.cols);
  • 创建三个辅助数组,用于辅助计算乘积结果
    • count:输入矩阵(Q)每一行非零元素个数。
    • index:Q每一行第一个非零元素的索引
    • answer:储存每次乘法运算的结果
    • 创建之后,分别先将三个数组进行初始化。
  • 初始准备工作做好后,就进行矩阵相乘。
  • 遍历当前矩阵(P)的行和Q的列
    • 在P的第i行和Q的第j列的乘法运算中,遍历P中第i行非零元素
    • 对于每个非零元素,根据索引c,从Q中对应列的第一个非零元素开始遍历
    • 将P中的非零元素的值与Q中对应的值相乘,并累加到answer数组中对应的列上
  • 将乘积结果中的非零元素储存在result矩阵的element数组中,并更新非零元素数量
  • 将result赋给P
  • 释放内存

相加

  • 判断相加合法性
  • 若可以,创建一个结果矩阵result,其行数、列数与当前矩阵相同
  • 初始化两个索引变量,用来遍历P、Q的非零元素
  • 进行相加运算:在循环中,比较P和Q中每个非零元素的索引值
    • P索引<Q索引,说明P的索引在Q中对应的数为0,则将当前矩阵P的非零元素添加到结果矩阵 result 中,并递增 it 和 result.terms
    • 如果两个矩阵的非零元素索引值相等,则将两个非零元素的值相加
      • 如果结果不为零,则将其添加到结果矩阵 result 中,并递增 it、ib 和 result.terms。
    • 如果 Q 的非零元素索引值小于当前矩阵P的非零元素索引值,则将 Q 的非零元素添加到结果矩阵 result 中,并递增 ib 和 result.terms。
  • 循环结束后,复制剩余的非零元素到结果矩阵 result 中。
  • 将结果矩阵 result 赋值P

输出

  • 先输出行列数
  • 使用两层嵌套循环遍历矩阵的每个元素位置。
    • 对于每个位置 (i, j),首先检查变量 k 是否小于非零元素的个数 terms,以确保还有未遍历的非零元素。
    • 如果当前非零元素的行索引等于 i+1(因为索引从0开始,而行号从1开始),并且列索引等于 j+1,则输出当前非零元素的值,并递增 k。
    • 如果当前位置不是非零元素的位置,即当前非零元素的行列索引与当前位置不匹配,则输出0。
  • 在内层循环结束后,输出一个换行符,表示已输出完当前行的元素。

矩阵转置

  • 首先,创建一个新的稀疏矩阵 b,其行、列数与当前矩阵相反,非零元素个数相等
  • 创建两个辅助数组 colSize 和 rowNext,用于实现转置操作。
    • colSize 数组的下标表示当前矩阵的每一列,存储的值表示对应列的非零元素个数
    • rowNext 数组的下标表示转置后矩阵的每一行,存储的值表示对应行的起始位置(在转置后的矩阵中的索引位置)。
  • 遍历当前矩阵的每一列,计算每列的非零元素个数,并存储在 colSize 数组中。
  • 使用动态规划的思想,计算转置后每一行的起始位置。
    • 初始化 rowNext[1] 为0,表示转置后的第一行起始位置为0。
    • 对于每个列 i(从第二列开始),通过累加前一列的非零元素个数来计算当前列的起始位置,存储在 rowNext[i] 中。
    • 进行转置复制操作,将当前矩阵的非零元素复制到转置后的矩阵 b 中。
  • 遍历当前矩阵的每个非零元素,获取其原始元素的行列索引和值
    • 根据转置后的行列索引,将元素复制到转置矩阵 b 的对应位置。
    • 根据 rowNext 数组的值,对转置矩阵 b 中的非零元素进行逐行排序。
  • 将转置后的矩阵 b 赋值给当前矩阵对象,以更新当前矩阵的内容。

AC代码

#include<iostream>
using namespace std;
const int SIZE=1000000;
//矩阵三元组定义
struct matrixTerm
{
	int row;
	int col;
	int value;
};
//稀疏矩阵类
class sparseMatrix 
{
    public:
        sparseMatrix(int r,int c);//构造函数
        ~sparseMatrix(){delete[] element;}//析构函数
        void reset(int n,int m);//1重置矩阵
        void multiply(sparseMatrix &Q);//2两个稀疏矩阵相乘,结果存放于P
		void add(sparseMatrix &Q);//3两个稀疏矩阵相加,结果存放于P
        void output();//4输出矩阵
        void transpose();//5矩阵转置
        sparseMatrix& operator=(sparseMatrix& a);//重载=,用得到
        void input(int x,int y,int v);//输入非0
    private:
    	int rows;//矩阵行数
		int cols;//矩阵列数
		int terms;//非0元素个数
        matrixTerm* element;//标记非零元素的行索引、列索引、值
};

//构造函数
sparseMatrix::sparseMatrix(int r,int c){
    rows=r;
    cols=c;
    terms=0;
    element=new matrixTerm[SIZE];
}
//更新三元组
void sparseMatrix::input(int x,int y,int v){
    matrixTerm a;
    a.row=x;
    a.col=y;
    a.value=v;
    element[terms]=a;
    terms++;
}
//1重置矩阵
void sparseMatrix::reset(int n,int m){
    rows=n;
    cols=m;
    terms=0;
    for(int i=1;i<=rows;i++){//行列的0都不用,从1开始
        for(int j=1;j<=cols;j++){
            int number;
            cin>>number;
            if(number!=0){//非零元素用按行优先顺序用一维数组记录 
                element[terms].row=i;
                element[terms].col=j;
                element[terms].value=number;
                terms++;
            }
        }
    }
}
//重载=
sparseMatrix& sparseMatrix::operator=(sparseMatrix& a)
	{//运算符重载 ,赋值*this = a 
		rows = a.rows;
        cols = a.cols;
        terms = a.terms;
        for (int i = 0; i < terms; i++)
            element[i] = a.element[i];
        return *this;
	}

//2两个稀疏矩阵相乘,结果存放于P
void sparseMatrix::multiply(sparseMatrix &Q){
    //判断是否能相乘
    if(cols!=Q.rows){//不合法
        *this=Q;//将Q赋给P
        cout<<"-1"<<endl;
        return ;
    }
    else{
        sparseMatrix result(rows,Q.cols);//保存结算结果
        int *count=new int[10000];//每一行非零元素的个数
        int *index=new int[10000];//第一个非零元素的索引
        int *answer=new int[10000];//结果
        //初始化
        for(int i=1;i<=Q.rows;i++)
            count[i]=0;
        
        //统计每一行的非零元素个数
        for(int i=0;i<Q.terms;i++){
            if(Q.element[i].value!=0)
                count[Q.element[i].row]++;
        }
        //计算第一个非零元素索引
        index[1]=0;
        for(int i=2;i<=Q.terms;i++){
            index[i]=index[i-1]+count[i-1];//用前一行的非零元素索引加上前一行非零元素的个数
        }
        //矩阵相乘
        int rIndex=0;//索引当前矩阵非零元素
        for(int i=1;i<=rows && rIndex<terms;i++){
            for(int j=1;j<=Q.cols;j++)
                answer[j]=0;//初始化
            while(rIndex<terms && element[rIndex].row==i){//找到当前行的非零元素
                int c=element[rIndex].col;
                if(count[c]!=0){//说明Q列有非零元素
                    for(int a=index[c];a<index[c]+count[c];a++)//从第一个非零元素开始
                        answer[Q.element[a].col]+=element[rIndex].value*Q.element[a].value;
                }
                rIndex++;
            }
            for(int k=1;k<=Q.cols;k++){//进行储存非零元素
                if(answer[k]!=0){
                    result.element[result.terms].row=i;
                    result.element[result.terms].col=k;
                    result.element[result.terms].value=answer[k];
                    result.terms++;
                }        
            }
        }
        *this=result;
        delete[] count;
        delete[] index;
        delete[] answer;
    }
}
//3两个稀疏矩阵相加,结果存放于P
void sparseMatrix::add(sparseMatrix &Q){
    //判断是否能相加
    if(Q.rows!=rows || Q.cols !=cols){
        *this=Q;
        cout<<"-1"<<endl;
        return;
    }
    else{
        sparseMatrix result(rows,cols);
        int it=0,ib=0;
        while(it!=terms && ib!=Q.terms){
            //每一行元素的行主索引+列数
            int tIndex=element[it].row*cols+element[it].col;
            int bIndex=Q.element[ib].row*cols+Q.element[ib].col;
            if(tIndex<bIndex){//Q的元素在后
                result.element[result.terms].col=element[it].col;
                result.element[result.terms].row=element[it].row;
                result.element[result.terms].value=element[it].value;
                it++;
                result.terms++;
            }
            else {
                if(tIndex==bIndex){//两项在同一个位置
                    if(element[it].value+Q.element[ib].value!=0){
                        result.element[result.terms].col=element[it].col;
                        result.element[result.terms].row=element[it].row;
                        result.element[result.terms].value=element[it].value+Q.element[ib].value;
                        result.terms++;
                    }
                    it++;
                    ib++;
                }
                else{//Q的索引在前
                    result.element[result.terms].col=Q.element[ib].col;
                    result.element[result.terms].row=Q.element[ib].row;
                    result.element[result.terms].value=Q.element[ib].value;
                    ib++;
                    result.terms++;
                }
            }
        }
        //复制剩余元素
        for(;it!=terms;it++)
		{
			result.element[result.terms].col = element[it].col;
			result.element[result.terms].row = element[it].row;
			result.element[result.terms].value = element[it].value;
			result.terms++;
		}
		for(;ib != Q.terms;ib++)
		{
			result.element[result.terms].col = Q.element[ib].col;
			result.element[result.terms].row = Q.element[ib].row;
			result.element[result.terms].value = Q.element[ib].value;
			result.terms++;
		}
		*this = result;
    }
}
//4输出矩阵
void sparseMatrix::output(){
    cout<<rows<<" "<<cols<<endl;
    int k=0;
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            if(k<terms && element[k].row==i+1 && element[k].col==j+1){//确保有未遍历的非零元素,并且行列索引对应匹配
                cout<<element[k].value<<" ";
                k++;
            }
            else
                cout<<0<<" ";
        }
        cout<<endl;
    }
}
//5矩阵转置
void sparseMatrix::transpose(){
    sparseMatrix b(cols,rows);
    b.terms=terms;
    int * colSize=new int[cols+1];//原矩阵每列非零元素个数
    int * rowNext=new int[cols+1];//转置后每一行的索引位置

    //计算*this每一列的非零元素
    for(int i=1;i<=cols;i++)
        colSize[i]=0;
    for(int i=0;i<terms;i++)
        colSize[element[i].col]++;

    //求出b中每一行的第一个非零元素索引
    rowNext[1]=0;
    for(int i=2;i<=cols;i++)
        rowNext[i]=rowNext[i-1]+colSize[i-1];
    
    //实施从*this到b的转置复制
    for(int i=0;i<terms;i++)
    {
        int j=rowNext[element[i].col];//转置后矩阵中当前非零元素的行索引
        //行列转置
        b.element[j].row = element[i].col;
        b.element[j].col = element[i].row;
        b.element[j].value = element[i].value; 
        rowNext[element[i].col] = rowNext[element[i].col] + 1;//以便在同一列中继续处理下一个非零元素的位置
    }
    *this = b;
}
int main(){
    sparseMatrix P(0,0);
    int w,m,n,t;
    int choice;
    cin>>w;
    for(int i=0;i<w;i++){
        cin>>choice;
        if(choice==1)
        {
            cin>>n>>m;
            P.reset(n,m);
        }
        else if(choice==2){
            cin>>n>>m;
            cin>>t;
            sparseMatrix Q1(n,m);
            for(int i=0;i<t;i++){
                int x,y,v;
                cin>>x>>y>>v;
                Q1.input(x,y,v);
            }
            P.multiply(Q1);
        }
        else if(choice==3){
            cin>>n>>m;
            cin>>t;
            sparseMatrix Q2(n,m);
            for(int i=0;i<t;i++){
                int x,y,v;
                cin>>x>>y>>v;
                Q2.input(x,y,v);
            }
            P.add(Q2);
        }
        else if(choice==4){
            P.output();
        }
        else if(choice==5)
            P.transpose();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值