三种特殊矩阵的压缩存储

  1. 矩阵通常采用二维数组形式来表示(逻辑结构)
  2. 特殊矩阵压缩到一维数组来节省存储空间
  3. 压缩原则:只存非零元素
  4. 规律分布矩阵:
    ① 按规律公式压缩:A[i][j]→B[k],二维下标[i][j]→一维下标[k],映射,地址计算问题;
    ② 稀疏矩阵:只存非零元素。

三角矩阵:

三角矩阵大体分为:下三角矩阵、上三角矩阵和对称矩阵。
一个n阶矩阵A:
若当i < j时,有aij=0,则称此矩阵为下三角矩阵;
若当i > j时,有aij=0,则此矩阵称为上三角矩阵;
若矩阵中的所有元素均满足aij = aji,则称此矩阵为对称矩阵。

下三角矩阵:
A的行主序序列:a11,a21,a22,a31,a32,a33…an1,an2…ann
元素个数为n(n+1)/2,可压缩存储到一个大小为n(n+1)/2的一维数组B中。
aij在一维数组中的位置为:
Loc[i][j] = Loc[1][1]+i*(i-1)/2+j-1 (i>=j)
Loc[i][j] = 0 (i<j)

上三角矩阵:
上三角也可压缩存储到n(n+1)/2个元素的一维数组C中。元素aij(i<j)在数组C中的存储位置为:
Loc[i][j]=Loc[1][1]+j*(j-1)/2+i-1

对角矩阵:
aij=aji,可以为每一对相等的元素分配一个存储空间,即只存下三角(或上三角)矩阵,从而将n2个元素压缩到n(n+1)/2个空间中。

带状矩阵:

矩阵中的所有非零元素都集中在以主对角线为中心的带状区域中。其中最常见的就是三对角带状矩阵。

在这里插入图片描述
行主序压缩存储方法:

  1. 确定存储该矩阵所需的一维向量空间大小:
    第一行和最后一行只有两个元素,其余各行均有3个非零元素。由此所需的一维向量空间大小为:3n-2
  2. 确定非零元素aij在一维数组空间中的位置:
    Loc[i][j]=Loc[1][1]+(3(i-1)-1)*size+(j-i+1)*size=Loc[1][1]+2(i-1)+j-1.

稀疏矩阵:

大多数元素为零的矩阵。
一般地,当非零元素个数只占矩阵元素总数的25%—30%及以下。
在这里插入图片描述
稀疏矩阵三元组表表示法:
稀疏矩阵的压缩存储:

  1. 存储非零元素值
  2. 存储该非零元素在矩阵中所处的行号和列号
    在这里插入图片描述
    在这里插入图片描述
    三元组表的类型定义:
#define MAXSIZE 1000  /*非零元素的个数最多为1000*/
typedef struct{
	int  row, col;  /*该非零元素的行下标和列下标*/
      ElementType  e; /*该非零元素的值*/
}Triple;
typedef struct{
	Triple  data[MAXSIZE+1];//非零元素三元组表,data[0]未用
	int m, n, len;  /*矩阵的行数、列数和非零元素的个数*/
}TSMatrix;

矩阵转置经典运算:
把位于(row,col)位置上的元素换到(col ,row)位置上。

Void TransMatrix(ElementType source[n][m], ElementType dest[m][n])
{/*Source、dest被转置矩阵和转置后的矩阵(用二维数组表示)*/
	int i, j;
	for(i = 0; i < m; i++)
     		for (j = 0; j < n; j++)
			dest[i][ j] =source[j] [i] ;
}

三元组表稀疏矩阵的转置运算:

换位:行列互换
矩阵source三元组表A的行、列互换得到B的元素;
重排:行主序排序
转置后的三元组表矩阵B也应以“行主序”存放,需对行、列互换后的三元组B,按行下标(A列下标)重新排序。
在这里插入图片描述
算法一:列序递增转置法
【算法思想】:
按被转置矩阵三元组表A的列序递增顺序进行转置。
从头到尾扫描表A第一列、第二列、…元素,依次送入表。
具体做法:
找出第k行全部元素:第k遍扫描三元组表A,找出其中所有col为k的三元组,转置后按顺序送到三元组表B中。
K=1,2,...,A.n (A的列数).
在这里插入图片描述
算法实现:

void TransposeTSMatrix(TSMatrix  A,  TSMatrix  *B) /*矩阵A三元组表转置到B所指向矩阵*/
{ 	
    int  i,j,k;  
    B->m=A.n; B->n=A.m; B->len=A.len;
	if(B->len > 0)
	{    
	     j = 1;//表B行号
	     for(k = 1; k <= A.n; k++) //按A列下标检索、排序
	         for(i = 1; i <= A.len; i++)//搜索整个A中列下标为k的元素存入B中
	         	if(A.data[i].col == k)
	           	{    
	                B->data[j].row = A.data[i].col;
		            B->data[j].col = A.data[i].row; 
		            B->data[j].e = A.data[i].e; 
		           j++; 	
		         }
    }
}

空间复杂度降低,时间复杂度不一定改善,T(n) = O(A.n * A.len)
所以此方法仅适用于存储稀疏矩阵。

算法二:一次性定位快速转置法
【算法思想】:
只用一重循环完成转置。对A中所有非零元“一次定位”直接放在B中的准确位置。
num[col]存放A三元组第col列非零元素个数。
position[col]存放A三元组第col列中第一个非零元素的位置。
position[col]=position[col-1]+num[col-1]

在这里插入图片描述

FastTransposeTSMatrix(TSMatrix  A,  TSMatrix  *B)
{ 	
    int col, t, p,q;  
    int num[MAXSIZE] = {0}, position[MAXSIZE];
	B->len = A.len; B->n = A.m; B->m = A.n ;
	if(B->len)
	{     
	    for(col = 1; col <= A.n; col++)  
	      num[col] = 0;
	    for(t = 1; t <= A.len; t++)  
	       num[A.data[t].col]++; /*累计每列元素个数*/
	    position[1] = 1;
     	for(col = 2; col <A.n; col++)  /*每列首元素在B表的位置*/
		   position[col] = position[col-1] + num[col - 1]; 
	    for(p = 1; p < A.len; p++)
	      {	col = A.data[p].col;  q = position[col]; //取列号,查位置(B下标)
		    B->data[q].row = A.data[p].col; 
		    B->data[q].col = A.data[p].row; //行列互换
		    B->data[q].e = A.data[p].e //元素赋值
		    position[col]++; 
		   }
	} 
}//更新该列元素插入位置

三元组表实现稀疏矩阵的乘法运算

矩阵M是m1×n1矩阵,矩阵N是m2×n2矩阵,当n1等于m2是,可以相乘。
经典算法:

for(i = 1; i <= m1; i++)
   for(j = 1; j <= n2; j++)
   {	
      Q[i][j] = 0;
      for(k = 1; k <= n1; k++)
   	  Q[i][j] += M[i][k] * N[k][j];
    }

三元组表矩阵乘法:
【算法思想】:
固定三元组M中元素(i,k,Mik),在三元组N中找所有行号为k的的对应元素(k,j,kj)进行相乘、累加得Q[i][j]
for(k = 1; k <= n1; k++)
Q[i][j] += M[i][k] * N[k][j]

#define  MAXSIZE 1000   /*非零元素的个数最多为1000*/
#define  MAXROW 1000  /*矩阵最大行数为1000*/

typedef struct
{	
     int row, col;  /*该非零元素的行下标和列下标*/
     ElementType e; /*该非零元素的值*/
}Triple;
typedef struct
{	
     Triple data[MAXSIZE+1];    /* 非零元素的三元组表,data[0]未用*/
	 int first[MAXROW+1];       /*三元组表中各行第一个非零元素所在的位置。*/
	 int m, n, len;         /*矩阵的行数、列数和非零元素的个数*/
}TriSparMatrix; 

int MulSMatrix(TriSparMatrix M,  TriSparMatrix N,  TriSparMatrix  *Q)/*采用改进的三元组表表示法,求矩阵乘积Q=M×N*/
int arow, brow, p; 
int ctemp[MAXSIZE];
if(M.n != N.m)   return FALSE;  /*返回FALSE表示求矩阵乘积失败*/
Q->m = M.m; Q->n = N.n; Q->len = 0;//初始化     
if(M.len * N.len != 0)//M、N都非空       
{	
    for(arrow = 1; arrow <= M.m; arow++)   /*逐行处理M*/
	{	
	    for(p = 1; p <= M.n; p++) 
	    ctemp[p] = 0 ;/* 当前行各元素累加器清零*/
	       Q->first[arow] = Q->len + 1;   
        for(p = M.first[arow]; p<M.first[arrow + 1]; p++)  /*p指向M当前行中每个非零元素*/
		{
		   brow=M.data[p].col;    /* M中的列号应与N中的行号相等*/
           if(brow<N.n)  
             t=N.first[brow+1];   
           else  
             t=N.len+1;
           for(q=N.first[brow];q<t;q++)
           {
             ccol=N.data[q].col;   /*乘积元素在Q中列号*/
             ctemp[ccol]+=M.data[p].e*N.data[q].e; } /* for q */
           }  /*求得Q中第crow行的非零元*/
           for(ccol=1;ccol<Q->n;col++)    /*压缩存储该非零元*/
	         if(ctemp[ccol])
	         {
	           if(++Q->len>MAXSIZE) return 0;
               Q->data[Q->len]={arow, ccol, ctemp[ccol]};
	         }/* if */   
	}/* for arow */
}/*if*/   
return(TRUE);      /*返回TRUE表示求矩阵乘积成功*/
} 

时间复杂度为O(A.n+A.len).

稀疏矩阵链式存储结构:十字链表

链式存储(十字链表)结构:
能够灵活地插入因运算而产生的新非零元素,删除新产生的零元素,实现矩阵的各种运算。

十字链表矩阵,每个非零元素用一个结点表示.

  • 除了(row,col,value)以外,还要有两个域:
  • right:链接同一行中的下一个非零元素;
  • down:链接同一列中的下一个非零元素。
    在这里插入图片描述
    在这里插入图片描述
    十字链表的结构类型:
typedef struct OLNode
{ 	int row, col;        /*非零元素的行和列下标*/
    ElementType value;
    struct OLNode * right,*down;  /*元素行、列后继链域*/
}OLNode; *OLink;

typedef struct 
{ 	OLink * row_head, *col_head;  /*行、列链表头指针向量*/
	int m, n, len;   /*矩阵行数、列数、非零元素个数*/
}CrossList; 

十字链表建立算法:

CreateCrossList (CrossList * M)
{
   if(M! = NULL)   free(M);
   scanf(&m, &n, &t);  /*输入行、列和非零元素的个数*/
   M->m = m; M->n = n; M->len = t;
   if(!(M->row_head = (Olink*)malloc((m+1)sizeof(OLink)))) exit(OVERFLOW);
   if(!(M->col_head = (OLink*)malloc((n+1)sizeof(OLink)))) exit(OVERFLOW);
   M->row_head[ ] = M->col_head[ ]=NULL;   /*初始化行、列头指针向量(数组)*/
   for(scanf(&i, &j, &e); i != 0; scanf(&i,&j,&e))
   {	
      if(!(p = (OLNode *) malloc(sizeof(OLNode)))) exit(OVERFLOW);
	  p->row = i; p->col = j; p->value = e;  /*生成结点*/
	  if(M->row_head[i] == NULL)   M->row_head[i] = p;//是该行第1个结点
	  else
	  {  /*寻找行表中的插入位置,列号排序*/
	     for(q = M->row_head[i];  q->right && q->right->col< j; q = q->right)
             p->right = q->right; q->right = p;  /*q跟踪插入位置前结点*/
      }
	  if(M->col_head[j] == NULL)   M->col_head[j] = p;
   	  else
   	  {  /*寻找列表中的插入位置*/
          for(q = M->col_head[j]; q->down && q->down->row < i; q = q->down)
             p->down = q->down; q->down = p;   /*完成插入*/
      }
    }
} 

时间复杂度为O(t×s).t为稀疏矩阵中非零元素个数,s=max(m,n).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值