用三列二維陣列表示的稀疏矩陣類

小許發表於2012-08-16

1.稀疏矩陣:矩陣中絕大多數的元素值為零,只有少數的非零元素值;

2.對稀疏矩陣採用壓縮儲存的目的是為了節省儲存空間,並且,稀疏矩陣壓縮儲存後,還要能夠比較方便地訪問其中的沒個元素(包括零元素和非零元素);

3.對稀疏矩陣進行壓縮儲存有兩種方法:稀疏矩陣的三列二維陣列表示和十字連結串列方法。

稀疏矩陣的三列二維陣列表示:

    (1)每個非零元素用三元組表示:(i,j,v)分別對應(行,列,非零元素);

    (2) 為了表示唯一性,除了每個非零元素用一個三元組表示外,在所有的非零元素三元組之前新增一組資訊(I,J,V)對應(總行數,總列數,非零元素個數),也就是說,我們要確定確切的稀疏矩陣,必須先知道該稀疏矩陣的總行數,總列數,非零個數;

    (3)為了便於在三列二維陣列B中訪問稀疏矩陣A中的各元素,通常還附設兩個長度與稀疏矩陣A的行數相同的向量POS與NUM。其中POS[K]表示稀疏矩陣A中的第k行的第一個非零元素(如果有的話)在三列二維組B中的行號;NUM[k]表示稀疏矩陣A中第k行中非零元素的個數;

pos[0]=0; pos[k]=pos[k-1]+num[k-1]

4.三列二維陣列表示的稀疏矩陣類如下:

檔名:X_Array.h

#include <iostream>
#include <iomanip>
using namespace std;
template <class T>
struct B
{
	int i;   //非零元素所在行號
	int j;   //非零元素所在列號
	T v;     //非零元素值
};
//三列二維陣列表示的稀疏矩陣類
template <class T>
class X_Array
{
private:
	int mm;      //稀疏矩陣行數
	int nn;      //稀疏矩陣列數
	int tt;      //非零個數
	B<T> *bb;    //三列二維陣列空間
	int *pos;    //某行第一個非零元素在b中的下標
	int *num;    //某行非零元素的個數
public:
	void in_X_Array();                    // 以三元組形式鍵盤輸入稀

	
	void cp_X_Array(int,int,int,B<T>[]);  // 複製三元陣列
	void th_X_Array(int,int,T[]);         // 由一般稀疏矩陣轉換
	void prt_X_Array();                   // 按行輸出稀疏矩陣
	X_Array tran_X_Array();               // 稀疏矩陣轉置
	X_Array operator +(X_Array &);        // 稀疏矩陣相加
	X_Array operator *(X_Array &);        // 稀疏矩陣相乘


};

// 以三元組形式鍵盤輸入稀疏矩陣非零元素

template <class T>
void X_Array<T>::in_X_Array()
{
	int k,m,n;
	cout<<"輸入行數 列數 非零元素個數:"<<endl;
	cin>>mm>>nn>>tt;
	bb=new B<T>[tt];
	cout<<"輸入行號 列號 非零元素值:"<<endl;
	for(k=0;k<tt;k++)
	{
		cin>>m>>n>>bb[k].v;
		bb[k].i=m-1;
		bb[k].j=n-1;
	}
	pos=new int[mm];
	num=new int[mm];
	for(k=0;k<mm;k++)  //num向量初始化
		num[k]=0;      //初始化mm行的每行num都設為零
	for(k=0;k<tt;k++)
		num[bb[k].i]=num[bb[k].i]+1;  //提取每個非零元素的行號,出現行號的陣列num值加1,當n個非零元素在同一行時,則加n次。
		pos[0]=0;
	for(k=1;k<mm;k++)       //構造pos向量
		pos[k]=pos[k-1]+num[k-1];  //pos[k]表示在稀疏矩陣第k行中第一個非零元素在陣列B中的行號
		return;
}

//複製三元陣列
template <class T>
void X_Array<T>::cp_X_Array(int m,int n,int t,B<T> b[])
{
	int k;
	mm=m;
	nn=n;
	tt=t;
	bb=new B<T>[tt];
	for(k=0;k<tt;k++)
	{
		bb[k].i=b[k].i-1;
		bb[k].j=b[k].j-1;
		bb[k].v=b[k].v;
	}
	pos=new int[mm];
	num=new int[mm];
	for(k=0;k<mm;k++)  //num向量初始化
		num[k]=0;      //初始化mm行的每行num都設為零
	for(k=0;k<tt;k++)
		num[bb[k].i]=num[bb[k].i]+1;  //提取每個非零元素的行號,出現行號的陣列num值加1,當n個非零元素在同一行時,則加n次。
		pos[0]=0;
	for(k=1;k<mm;k++)       //構造pos向量
		pos[k]=pos[k-1]+num[k-1];  //pos[k]表示在稀疏矩陣第k行中第一個非零元素在陣列B中的行號
		return;

}
// 由一般稀疏矩陣轉換
template <class T>
void X_Array<T>::th_X_Array(int m,int n,T a[])
{
	int k,t=0,p,q;
	T d;
	for(k=0;k<m*n;k++)          //統計非零個數
		if(a[k]!=0) t++;  
	mm=m;nn=n;tt=t;
	bb=new B<T>[tt];
	k=0;
	for(p=0;p<m;p++)
		for(q=0;q<n;q++)
		{
			d=a[p*n+q];
			if(d!=0)             //非零元素
			{
				bb[k].i=p;
				bb[k].j=q;
				bb[k].v=d;
				k=k+1;
			}
		}
		pos=new int[mm];
		num=new int[mm];
		for(k=0;k<mm;k++)  //num向量初始化
			num[k]=0;      //初始化mm行的每行num都設為零
		for(k=0;k<tt;k++)
			num[bb[k].i]=num[bb[k].i]+1;  //提取每個非零元素的行號,出現行號的陣列num值加1,當n個非零元素在同一行時,則加n次。
			pos[0]=0;
		for(k=1;k<mm;k++)       //構造pos向量
			pos[k]=pos[k-1]+num[k-1];  //pos[k]表示在稀疏矩陣第k行中第一個非零元素在陣列B中的行號
			return;

}
// 按行輸出稀疏矩陣
template <class T>
void X_Array<T>::prt_X_Array()
{
	int k,kk,p;
	for(k=0;k<mm;k++)                         //按行輸出
	{
		p=pos[k];
		for(kk=0;kk<nn;kk++)                  //輸出一行
			if((bb[p].i==k)&&(bb[p].j==kk))   //輸出非零元素
			{
				cout<<setw(8)<<bb[p].v;p=p+1;
			}
			else
				cout<<setw(8)<<0;
		cout<<endl;
	}
	return;

}
// 稀疏矩陣轉置
template <class T>
X_Array<T> X_Array<T>::tran_X_Array()
{
	X_Array<T> at;                                 //定義轉置矩陣物件
	int k,p,q;
	at.mm=nn;
	at.nn=mm;
	at.tt=tt;
	at.bb=new B<T>[tt];
	k=0;
	for(p=0;p<nn;p++)                              //按列掃描所有非零元素
		for(q=0;q<tt;q++)
		{
			if(bb[q].j==p)                         //  將非零元素一次存放到轉置矩陣的三列二維陣列中               
			{
				at.bb[k].i=bb[q].j;
				at.bb[k].j=bb[q].i;
				at.bb[k].v=bb[q].v;
				k++;
			}
		}
		at.pos=new int[at.mm];
		at.num=new int[at.mm];
		for(k=0;k<at.mm;k++)
			at.num[k]=0;
		for(k=0;k<at.tt;k++)
			at.num[at.bb[k].i]=at.num[at.bb[k].i]+1;
		at.pos[0]=0;
		for(k=1;k<at.mm;k++)
			at.pos[k]=at.pos[k-1]+at.num[k-1];
		return (at);                                      //返回轉置矩陣
}

// 稀疏矩陣相加
template <class T>
X_Array<T> X_Array<T>::operator+ (X_Array<T> &b)
{
	X_Array<T> c;
	B<T> *a;
	T d;
	int m,n,k,p;
	if((mm!=b.mm)||(nn!=b.nn))
		cout<<"不能相加!"<<endl;
	else
	{
		a=new B<T>[tt+b.tt];                         //臨時申請一個三列二維陣列空間
		p=0;
		for(k=0;k<mm;k++)                            //逐行處理
		{
			m=pos[k];n=b.pos[k];
			while((bb[m].i==k)&&(b.bb[n].i==k))        //行號相同
			{
				if(bb[m].j==b.bb[n].j)                 //列號相同則相加
				{
					d=bb[m].v+b.bb[n].v;
					if(d!=0)                           //相加後非零
					{
						a[p].i=k;a[p].j=bb[m].j;
						a[p].v=d;p=p+1;
					}
					m=m+1;n=n+1;
				}
				else if(bb[m].j<b.bb[n].j)            //列號不同則複製列號小的一項
				{
					a[p].i=k;a[p].j=bb[m].j;
					a[p].v=bb[m].v;p=p+1;
					m=m+1;

				}
				else                                 //列號不同複製另一項
				{
					a[p].i=k;a[p].j=b.bb[m].j;
					a[p].v=b.bb[m].v;p=p+1;
					n=n+1;

				}
			}
			while(bb[m].i==k)                       //複製矩陣中本行剩餘的非零元素
			{
				a[p].i=k;a[p].j=bb[m].j;
				a[p].v=bb[m].v;p=p+1;
				m=m+1;

			}
			while(b.bb[n].i==k)                    //複製另一矩陣中本行剩餘的非零元素
			{
				a[p].i=k;a[p].j=b.bb[m].j;
				a[p].v=b.bb[m].v;p=p+1;
				n=n+1;

			}
		}
		c.mm=mm;c.nn=nn;c.tt=p;
		c.bb=new B<T>[p];                          //申請一個三列二維陣列空間
		for(k=0;k<p;k++)                           //複製臨時三列二維陣列空間中的元素
		{
			c.bb[k].i=a[k].i;
			c.bb[k].j=a[k].j;
			c.bb[k].v=a[k].v;
		}
		delete a;                                    //釋放臨時三列二維陣列空間
		c.pos=new int[c.mm];
		c.num=new int[c.mm];
		for(k=0;k<c.mm;k++)
			c.num[k]=0;
		for(k=0;k<c.tt;k++)
			c.num[c.bb[k].i]=c.num[c.bb[k].i]+1;
		c.pos[0]=0;
		for(k=1;k<c.mm;k++)
			c.pos[k]=c.pos[k-1]+c.num[k-1];

	}
	return c;
}
template <class T>
X_Array<T> X_Array<T>::operator* (X_Array<T> &b)
{
	X_Array<T> cc;
	int k,m,n,p,t;
	T *c;                  //定義乘積矩陣
	if(nn!=b.mm)
		cout<<"兩矩陣無法相乘!"<<endl;
	else
	{
		c=new T[mm*b.nn];  //申請乘積矩陣的臨時空間
		k=0;
		for(m=0;m<mm;m++)     
		{
			for(n=0;n<b.nn;n++)
			{
				c[k]=0;k=k+1;  //乘積矩陣元素初始化為零
			}
		}
		for(m=0;m<tt;m++)     //對於左矩陣中的每一個非零元素
		{
			k=bb[m].j;        //左矩陣中非零元素的列值K
			n=b.pos[k];       //右矩陣中行號與K相同的第一個非零元素的位置
			t=b.pos[k]+b.num[k];//右矩陣中行號與k相同的最後一個非零元素的位置
			while(n!=t)
			{
				p=bb[m].i*b.nn+b.bb[n].j;//在乘積矩陣中的位置
				c[p]=c[p]+bb[m].v*b.bb[n].v;//累加非零元素的乘積
				n=n+1;
			}

		}
		cc.th_X_Array(mm,b.nn,c); //由一般的稀疏矩陣轉換成用三列二維陣列表示
		delete c;                 //釋放乘積矩陣的臨時空間
	}
	return cc;
}



2.例項應用:

#include "test2.h"
#include<stdlib.h>
int main()
{
	B<double> a[8]={{1,3,3.0},{1,8,1.0},{3,1,9.0},{4,5,7.0},{5,7,6.0},{6,4,2.0},{6,6,3.0},{7,3,5.0}};
	X_Array<double> x,y,z,xt,c;
	x.cp_X_Array(7,8,8,a);
	cout<<"輸出稀疏矩陣X:"<<endl;
	x.prt_X_Array();
	xt=x.tran_X_Array();

	cout<<"輸出稀疏矩陣x的轉置xt:"<<endl;
	xt.prt_X_Array();
	y.in_X_Array();
	cout<<"輸出稀疏矩陣y:"<<endl;
	y.prt_X_Array();
	z=x+y;
	cout<<"輸出稀疏矩陣z=x+y"<<endl;
	z.prt_X_Array();
	c=x*xt;
	cout<<"輸出稀疏矩陣c=x*y"<<endl;
	c.prt_X_Array();
	system("pause");
	return 0;
}


3.實驗結果:

 

 

相關文章