Spiga

数组(三):稀疏矩阵

2012-05-17 20:21:40

一、稀疏矩阵的定义

  对于那些零元素数目远远多于非零元素数目,并且非零元素的分布没有规律的矩阵称为稀疏矩阵(sparse)。
  人们无法给出稀疏矩阵的确切定义,一般都只是凭个人的直觉来理解这个概念,即矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素没有分布规律。
    

二、稀疏矩阵的压缩存储

  由于稀疏矩阵中非零元素较少,零元素较多,因此可以采用只存储非零元素的方法来进行压缩存储。
  由于非零元素分布没有任何规律,所以在进行压缩存储的时侯需要存储非零元素值的同时还要存储非零元素在矩阵中的位置,即非零元素所在的行号和列号,也就是在存储某个元素比如aij的值的同时,还需要存储该元素所在的行号i和它的列号j,这样就构成了一个三元组(i,j,aij)的线性表。
  三元组可以采用顺序表示方法,也可以采用链式表示方法,这样就产生了对稀疏矩阵的不同压缩存储方式。

a、稀疏矩阵的顺序实现

  若把稀疏矩阵的三元组线性表按顺序存储结构存储,则称为稀疏矩阵的三元组顺序表。
  顺序表中除了存储三元组外,还应该存储矩阵行数、列数和总的非零元素数目,这样才能唯一的确定一个矩阵。

    

  顺序存储结构存储三元组线性表的C#代码如下:

struct tupletype<T>
{
	public int i;//行号
	public int j;//列号
	public T v; //元素值
	public tupletype(int i, int j, T v)
	{
		this.i = i;
		this.j = j;
		this.v = v;
	}
}
class spmatrix<T>
{
	int MAXNUM;//非零元素的最大个数
	int md;//行数值
	int nd;//列数值
	int td;//非零元素的实际个数
	tupletype<T>[] data;//存储非零元素的值及一个表示矩阵行数、列数和总的非零元素数目的特殊三元组
}

b、稀疏矩阵的十字链表实现

    十字链表结点分为三类 :
  表结点,它由五个域组成,其中i和j存储的是结点所在的行和列,right和down存储的是指向十字链表中该结点所有行和列的下一个结点的指针,v用于存放元素值。
  行头和列头结点,这类结点也有域组成,其中行和列的值均为零,没有实际意义,right和down的域用于在行方向和列方向上指向表结点,next用于指向下一个行或列的表头结点。
  总表头结点,这类结点与表头结点的结构和形式一样,只是它的i和j存放的是矩阵的行和列数。

    

  十字链表可以看作是由各个行链表和列链表共同搭建起来的一个综合链表,每个结点aij既是处在第i行链表的一个结点,同时也是处在第j列链表上的一个结点,就你是处在十字交叉路口上的一个结点一样,这就是十字链表的由来。
  十字链表中的每一行和每一列链表都是一个循环链表,都有一个表头结点。
    
三、稀疏矩阵的实现

  矩阵运算通常包括矩阵转置、矩阵加、矩阵乘、矩阵求逆等。这里仅讨论最简单的矩阵转置运算算法。
  矩阵转置运算是矩阵运算中最重要的一项,它是将m×n的矩阵变成另外一个n×m的矩阵,使原来矩阵中元素的行和列的位置互换而值保持不变,即若矩阵N是矩阵M的转置矩阵,则有:M[i][j]=N[j][i] (0≤i≤m-1,0≤j≤n-1)。

三元组表表示转置矩阵的具体方法是:
  第一步:根据M矩阵的行数、列数和非零元总数确定N矩阵的列数、行数和非零元总数。
  第二步:当三元组表非空(M矩阵的非零元不为0)时,对M中的每一列col(0≤col≤n-1),通过从头至尾扫描三元组表data,找出所有列号等于col的那些三元组,将它们的行号和列号互换后依次放人N的data中,即可得到N的按行优先的压缩存贮表示。

class spmatrix<T>
{
	int MAXNUM;//非零元素的最大个数
	int md;//行数值
	int nd;//列数值
	int td;//非零元素的实际个数
	tupletype<T>[] data;//存储非零元素的值及一个表示矩阵行数、列数和总的非零元素数目的特殊三元组
	public int Md
	{
		get
		{
			return md;
		}
		set
		{
			md = value;
		}
	}
	public int Nd
	{
		get
		{
			return nd;
		}
		set
		{
			nd = value;
		}
	}
	public int Td
	{
		get
		{
			return td;
		}
		set
		{
			td = value;
		}
	}
	//三元组表的data属性
	public tupletype<T>[] Data
	{
		get
		{
			return data;
		}
		set
		{
			data = value;
		}
	}
	//初始化三元组顺序表
	public spmatrix() { }
	public spmatrix(int maxnum, int md, int nd)
	{
		this.MAXNUM = maxnum;
		this.md = md;
		this.nd = nd;
		data = new tupletype<T>[MAXNUM];
	}
	//设置三元组表元素的值
	public void setData(int i, int j, T v)
	{
		data[td] = new tupletype<T>(i, j, v);
		td++;
	}
	//矩阵转置算法
	public spmatrix<T> Transpose()
	{
		spmatrix<T> N = new spmatrix<T>();
		int p, q, col;
		N.MAXNUM = MAXNUM;
		N.nd = md;
		N.md = nd;
		N.td = td;
		N.data = new tupletype<T>[N.td];
		if (td != 0)
		{
			q = 0;//控制转置矩阵的下标
			for (col = 0; col < nd; col++)//扫描矩阵的列
			{
				for (p = 0; p < td; p++)//p控制被转置矩阵的下标
				{
					if (data[p].j == col)
					{
						N.data[q].i = data[p].j;
						N.data[q].j = data[p].i;
						N.data[q].v = data[p].v;
						q++;
					}
				}
			}
		}
		return N;
	}