三元组压缩存储稀疏矩阵的C++类模板实现

一 简介


设m*n 矩阵中有t 个非零元素且t<<m*n,这样的矩阵称为稀疏矩阵。

遇到阶数很高的大型稀疏矩阵,按常规分配方法,顺序分配在计算机内,占用内存过大。

故有另一种存储方法,仅仅存放非零元素。

但对于这类矩阵,通常零元素分布没有规律,为能找到相应的元素,仅存储非零元素是不够的,还要记下它所在的行和列。


将三元组按行优先的顺序,同一行中列号从小到大的规律排列成一个线性表,称为三元组表,采用顺序存储方法存储该表。一张图举例说明问题:




二 编程实现


准备的测试文件有:









1. 先建立一个CPMatrix.h

#ifndef CPMatrix_H
#define CPMatrix_H
enum Sub_Or_Add{
	Sub,Add//在下面成员函数选择加法或减法的标志
};
template <typename T>
struct Triple{//三元组类型结构体
	int i,j;//行列下标
	T e;//非零元素的值
};
const int MAX_SIZE=100;//非零元素的个数最大值
const int MAX_RC=20;//矩阵最大行数
template <typename T>
class CPMatrix{
private:
	Triple<T> data[MAX_SIZE+1];//非零元三元组表,data[0]不用
	int rpos[MAX_RC+1];//各行第一个非零值的位置表
	int row,col,num;//行列数,非零元个数
public:
	CPMatrix(){
		row=col=num=1;
	}
	CPMatrix(char * FileName){
		CreateSMatrixFromFile(FileName);
	}
	bool CreateSMatrixFromFile(char *FileName="test1.txt"){
		int i,j;
		ifstream fin(FileName);
		fin>>row>>col>>num;
		if(num>MAX_SIZE||row>MAX_RC)
			return false;
		data[0].i=0;
		for(i=1;i<=num;i++){
			fin>>data[i].i>>data[i].j>>data[i].e;
			if(data[i].i<1||data[i].i>row||data[i].j<1
			||data[i].j>col)
				return false;
			if(data[i].i<data[i-1].i||data[i].i==data[i-1].i
			&&data[i].j<data[i-1].j)//行或者列输入顺序错误!!
				return false;
		}
			fin.close();
			for(i=1;i<=row;i++){
				rpos[i]=1;//先初始化:每一行第一个非零值元素都挤在三元组第一个位置data[1],显然不可能
			}
			for(i=1;i<=num;i++)
				for(j=data[i].i+1;j<=row;j++)//从非零元素所在行的下一行开始
					rpos[j]++;//相当于每行第一个非零元素的位置后移一位
			return true;
		
	}
	void CopySMatrix(const CPMatrix<T> &M){
		int i;
		row=M.row;
		col=M.col;
		num=M.num;
		for(i=0;i<=M.num;i++)
			data[i]=M.data[i];
		for(i=0;i<M.row;i++)
			rpos[i]=M.rpos[i];
	}
	void TransposeSMatrix(const CPMatrix<T> &M){
		int i,j,k,colm[MAX_RC+1];//[0]不用!!
		row=M.col;
		col=M.row;
		num=M.num;
		if(num){//矩阵非空
			for(i=1;i<=row;++i)//从矩阵第一列到最后一列
				colm[i]=0;
			for(i=1;i<=num;++i)
				++colm[M.data[i].j];//统计每一列有多少个元素,最后colm[n]=m表示第n列有m个非零元素
			rpos[1]=1;
			for(i=2;i<=row;++i)
				rpos[i]=rpos[i-1]+colm[i-1];//最后rpos[i]=a表示M转置后第i行第一个非零元素的位置data[a]
			for(i=1;i<=row;++i)
				colm[i]=rpos[i];//此时colm[]存放每一行第一个非零元素在M转置后应该存放的位置data[]
			for(i=1;i<=num;++i){//对于每一个非零元素
				j=M.data[i].j;//取得行号
				k=colm[j]++;//k代表该行第n个非零元素的位置,colm[]自增准备下次该行后一个非零元素的读取
				data[k].i=j;//开始对调
				data[k].j=M.data[i].i;
				data[k].e=M.data[i].e;
			}
		}
		
	}
	bool Sub_Add_SMatrix(const CPMatrix<T> &M,const CPMatrix<T> &N,Sub_Or_Add mod){
		int p,q,up,uq;
		if(M.row!=N.row||M.col!=N.col)
			return false;
		row=M.row;
		col=M.col;
		num=0;
		for(int k=1;num<=MAX_SIZE&&k<=M.row;++k)//k指示行号
		{
			rpos[k]=num+1;//“和矩阵”的第K行的第一个元素的位置data[]
			p=M.rpos[k];//p指示M第K行当前元素的位置
			q=N.rpos[k];
			if(k<M.row){//不是最后一行
				up=M.rpos[k+1];//下一行的第一个元素位置data[n]的n是本行元素的上界最大
				uq=N.rpos[k+1];
			}
			else{
				up=M.num+1;//最后一行也设置上届
				uq=N.num+1;
			}
			while(p<up&&q<uq)
				if(M.data[p].j<N.data[q].j){//M当前元素的列 < N当前元素的列
					data[++num] = M.data[p++];//不论加法减法都是把M赋给“和矩阵”
					
				}
					
				else
					if(M.data[p].j>N.data[q].j){//M当前元素的列 > N当前元素的列,就是N在前面
						data[++num]=N.data[q++];//加法直接赋值
						if(mod==Sub)
							data[num].e *= (-1);//减法还要乘-1,应为相当于用0减去!!!
					}
						
					else{
						if(M.data[p].e-N.data[q].e!=0&&mod==Sub){
							data[++num]=M.data[p];
							data[num].e-=N.data[q].e;
						}
						if(M.data[p].e+N.data[q].e!=0&&mod==Add){
							data[++num]=M.data[p];
							data[num].e+=N.data[q].e;
						}
						p++;
						q++;
					}

			while(p<M.rpos[k+1]&&p<=M.num)
				data[++num]=M.data[p++];
			while(q<N.rpos[k+1]&&q<=N.num){
				data[++num] = N.data[q++];
				if(mod==Sub)
					data[num].e *= (-1);
			}
		}
		if(num>MAX_SIZE) return false;
		else return true;
	}
	bool MultSMatrix(const CPMatrix<T> &M,CPMatrix<T> &N){
		int i,j,q,p,up,uq;
		T temp;//暂时存放data[].e
		CPMatrix<T> E;//存放N转置矩阵
		if(M.col!=N.row) return false;
		row=M.row;
		col=N.col;
		num=0;
		E.TransposeSMatrix(N);
		for(i=1;i<=row;i++)//对于乘积每一行
			for(j=1;j<=col;j++){//对于乘积每一列
				temp=0;
				p=M.rpos[i];//p指示M在第i行的第一个非零元素位置
				q=E.rpos[j];//q指示E在第j行(也就是N在第j列)的第一个非零元素位置
				if(i<M.row)
					up=M.rpos[i+1];
				else
					up=M.num+1;
				if(j<E.row)
					uq=E.rpos[j+1];
				else uq=E.num+1;
				while(p<up&&q<uq)
					if(M.data[p].j<E.data[q].j)
						p++;
					else
						if(M.data[p].j>E.data[q].j)
							q++;
						else
							temp+=M.data[p++].e*E.data[q++].e;
				if(temp){
					if(++num>MAX_SIZE) return false;
					data[num].i=i;
					data[num].j=j;
					data[num].e=temp;
				}
			}
			return true;
	}
	void PrintSMatrix()const{
		int k=1;
		const Triple<T> *p=data+1;//p指向第一个非零元素
		if(num==0) return ;
		for(int i=1;i<=row;i++){
			for(int j=1;j<=col;j++){
				if(k<=num&&p->i==i&&p->j==j){
					cout<<setw(3)<<(p++)->e;
					k++;
				}
				else cout<<setw(3)<<0;
			}
			cout<<endl;
		}
	}
};
#endif



2. 当然习惯了C.h包含各种有用没用的头文件(可多不可少)


//C.h 几乎各程序都需要用到的文件包含宏命令和使用名空间
#ifndef C_H
#define C_H
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <string>
#include <vector>
#include <list>
#include <stack>
#include <queue>
#include <bitset>
#include <algorithm>
#include <ctime>
#include <cstdarg>
#include <assert.h>
using namespace std;
#endif



3. 最后是测试


#include "C.h"
#include "CPMatrix.h"
int main()
{
	bool f;
	CPMatrix<int> A("test2.txt"), B, C,D;
	cout<<"test2.txt建矩阵A="<<endl;
	A.PrintSMatrix();
	f=B.CreateSMatrixFromFile();
	if(f)
	{
		cout<<"默认test1.txt建矩阵B="<<endl;
		B.PrintSMatrix();
	}
	C.CopySMatrix(A);
	cout<<"A的拷贝C="<<endl;
	C.PrintSMatrix();
	f=C.CreateSMatrixFromFile("test3.txt");
	if(f)
	{
		cout<<"由test3.txt重建C="<<endl;
		C.PrintSMatrix();
	}
	f=B.Sub_Add_SMatrix(A, C,Add);
	if(f)
	{
		cout<<"B=A+C="<<endl;
		B.PrintSMatrix();
	}
	f=D.Sub_Add_SMatrix(B, A,Sub);
	if(f)
	{
		cout<<"D=B-A="<<endl;
		D.PrintSMatrix();
	}
	A.TransposeSMatrix(C);
	cout<<"C的转置重建A:"<<endl;
	A.PrintSMatrix();
	f=B.MultSMatrix(C, A);
	if(f)
	{
		cout<<"B=C×A="<<endl;
		B.PrintSMatrix();
	}
	return 0;
}


运行结果:




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值