1. 程式人生 > >資料結構-稀疏矩陣-靜態分配的三元組順序儲存

資料結構-稀疏矩陣-靜態分配的三元組順序儲存

假設在m*n的矩陣中,有t個元素不為0,令a=t/m*n 稱a為矩陣的稀疏因子。通常認為 a<= 0.05時稱為稀疏矩陣。

按照壓縮儲存的概念,只儲存稀疏矩陣的非零元。因此,除了儲存非零元的值之外,還必須同時幾下它所在行和列的位置(i,j)。反之

一個三元組(i,j,aij)唯一確定了矩陣A的一個非零元。因此,稀疏矩陣可由表示非零元的三元組及其行列數唯一確定。

首先是輔助巨集的定義:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define UNDERFLOW -2
#define MAXSIZE 12500 //假設非零元個數的最大值為12500
#define MAXMN   12500//假設最大列數 12500

稀疏矩陣的三元組順序表儲存表示:

//稀疏矩陣的三元組順序表儲存表示
typedef struct{ 
    int i,j; //該非0元的行下標和列下標 
    ElemType e;//非0元的值
}Triple;//三元組型別
typedef struct{
    Triple data[MAXSIZE+1]; //非零元三元組表,data[0] 未採用
	int rpos[MAXMN+1]; //記錄各行第一個非0元的位置
    int mu,nu,tu;//矩陣的行數 列數 和非零元個數
}RLSMatrix;//行邏輯連結順序表型別

建立稀疏矩陣M.

Status CreateSMatrix(RLSMatrix &M){
	//建立稀疏矩陣M
    int i,j,a,flag=0;
    M.tu=0;
    scanf("%d %d",&M.mu,&M.nu);
	if(M.mu<1||M.nu<1)
		return ERROR;
    for(i=1;i<=M.mu;i++){
		M.rpos[i]=0;
	    for(j=1;j<=M.nu;j++){
		   scanf("%d",&a);
		   if(a!=0) {
		      M.tu++; //數目增加
			  if(!flag) {//flag==0判斷是這一行第一個元素 是就賦給M.rpos 並將flag賦1
			     M.rpos[i]=M.tu;
			     flag=1;
			  }
		      M.data[M.tu].i=i;
			  M.data[M.tu].j=j;
		      M.data[M.tu].e=a;
		   }
		}
	    flag=0;//下一行flag賦0
	}    
     return OK;      
}

銷燬稀疏矩陣M.

Status DestroySMatrix(RLSMatrix &M){
	//銷燬稀疏矩陣M
    M.mu=0;
    M.nu=0;
    M.tu=0; 
    return OK;
}

輸出稀疏矩陣M。

void PrintRLSMatrix(RLSMatrix &M){
	//輸出稀疏矩陣M
    int i,j,k=1;
	if(!M.mu||!M.nu||!M.tu)
       printf("空矩陣\n");
	else{
	    for(i=1;i<=M.mu;i++){
	       for(j=1;j<=M.nu;j++){
	           if(M.data[k].i==i&&M.data[k].j==j) //行數列數相等
				   printf("%3d",M.data[k++].e);
	           else
			       printf("  0");//否則輸出0
		   } 
	       printf("\n");  
		} 
	}
	printf("\n"); 
}

由稀疏矩陣M複製得到T.

Status CopySMatrix(RLSMatrix M,RLSMatrix &T){
	//由稀疏矩陣M複製得到T
	T=M;
    return OK;
}

稀疏矩陣Q=M+N.

Status AddSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
	//稀疏矩陣Q=M+N
    if(M.mu!=N.mu||M.nu!=N.nu) //M與N 的行數或列數不相等
         return ERROR;
    int i,j,k1=1,k2=1;//k1 M中元素位置 k2 N中元素位置
	Q.tu=0;
	Q.mu=M.mu;
	Q.nu=N.nu;
	for(i=1;i<=M.mu;i++)
       for(j=1;j<=M.nu;j++){
		   if(M.data[k1].i==i&&M.data[k1].j==j){//如果M此位置元素非零
			   if(N.data[k2].i==i&&N.data[k2].j==j){//如果N此位置元素也非零   
				   if(M.data[k1].e+N.data[k2].e){ //如果兩者相加和不為0 賦給Q.data
                      Q.tu++;    
				      Q.data[Q.tu].i=i;
                      Q.data[Q.tu].j=j;
				      Q.data[Q.tu].e=M.data[k1].e+N.data[k2].e;
				  }			           
			       k1++; //下一個非零元素
			       k2++;    
			  } 
	          else { //N中此位置元素為0 
			      Q.tu++;
				  Q.data[Q.tu].i=i;
                  Q.data[Q.tu].j=j;
				  Q.data[Q.tu].e=M.data[k1++].e;    //M中元素賦給Q,data
			  }			         
		  } 
		   else if(N.data[k2].i==i&&N.data[k2].j==j){//如果N中此位置有非零元素  且M中此位置為0
              Q.tu++;
		      Q.data[Q.tu].i=i;
              Q.data[Q.tu].j=j;
		      Q.data[Q.tu].e=N.data[k2++].e; //N中元素賦給Q,data
		  }  
	   }
    return OK;
}

求稀疏矩陣的差Q=M-N.

Status SubtMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
	//求稀疏矩陣的差Q=M-N
    if(M.mu!=N.mu||M.nu!=N.nu)//矩陣的行或列不相等
         return ERROR;
    int i,j,k1=1,k2=1;//k1 M中元素位置 k2 N中元素位置
	Q.tu=0;
	Q.mu=M.mu;
	Q.nu=N.nu;
	for(i=1;i<=M.mu;i++)
       for(j=1;j<=M.nu;j++){
		   if(M.data[k1].i==i&&M.data[k1].j==j){ //如果M此位置元素非零
			   if(N.data[k2].i==i&&N.data[k2].j==j){//如果N此位置元素也非零		 
				   if(M.data[k1].e-N.data[k2].e){//如果兩者相加差不為0 賦給Q.data
                      Q.tu++;
                      Q.data[Q.tu].i=i;
                      Q.data[Q.tu].j=j;
                      Q.data[Q.tu].e=M.data[k1].e-N.data[k2].e;
				   }  			           
			       k1++;//下一個非零元素
			       k2++;    
			  } 
	          else{//N中此位置元素為0 
                  Q.tu++;
                  Q.data[Q.tu].i=i;
                  Q.data[Q.tu].j=j;
			      Q.data[Q.tu].e=M.data[k1++].e;        //M中元素賦給Q,data
			  }			     
		  } 
		   else if(N.data[k2].i==i&&N.data[k2].j==j){//如果N中此位置有非零元素  且M中此位置為0
			  Q.tu++;
              Q.data[Q.tu].i=i;//N中元素賦給Q,data
              Q.data[Q.tu].j=j;
		      Q.data[Q.tu].e=N.data[k2++].e*-1; 
		  }
	   }
    return OK;
}

求稀疏矩陣的轉置矩陣T.

列號col 從1變到M.tu 掃描M.data每個元素 對於列號等於j的三元組,將其行標列標互換後依次放入T.data中.

Status TransposeSMatrix(RLSMatrix M,RLSMatrix &T)
{
	/*求稀疏矩陣的轉置矩陣T
	列號col 從1變到M.tu 掃描M.data每個元素 對於列號等於j的三元組
    ,將其行標列標互換後依次放入T.data中*/
    T.mu=M.nu;
    T.nu=M.mu;
	T.tu=M.tu;
	if(T.tu)//如果不是空矩陣{
	   int j,k=1,q=1;//q對目標三元組順序表當前元素計數
	   for(j=1;j<=M.nu;j++)
	      for(k=1;k<=M.tu;k++)
			  if(M.data[k].j==j){//如果當前元素列數等於j M的元素賦給T.data 並把i,j互換
			      T.data[q].e=M.data[k].e;
			      T.data[q].i=M.data[k].j;
			      T.data[q].j=M.data[k].i;
			      q++;
			  }
	} 
    return OK;
}

複雜度O(M.nu*M.tu),當足夠稀疏時演算法更有效,否則最壞O(M.nu^2*M.mu)。

關鍵原因在於要重複遍歷多次順序表,能否先遍歷一次,求出A的元素在B中應該有的位置,之後直接放入呢?

快速求稀疏矩陣的轉置矩陣T:

確定M中的每一列的非零元素的個數 計入陣列num M中第col列的第一個元素在T中的位置cpot[col]滿足 cpot[col]=cpot[col-1]+num[col-1] cpot[1]=1  用p遍歷M 第一次遇到列標為col的元素 放入T中第cpot[col] 個位置 沒插入一個cpot[col]加一 後遇到clo直接插入cpot[col]。

Status FastTransposeSMatrix(RLSMatrix M,RLSMatrix &T)
{
	/*快速求稀疏矩陣的轉置矩陣T
	確定M中的每一列的非零元素的個數 計入陣列num
	M中第col列的第一個元素在T中的位置cpot[col]滿足 cpot[col]=cpot[col-1]+num[col-1] cpot[1]=1
	用p遍歷M 第一次遇到列標為col的元素 放入T中第cpot[col] 個位置 沒插入一個cpot[col]加一 後遇到clo直接插入cpot[col]*/
     T.mu=M.nu;
     T.nu=M.mu;
     T.tu=M.tu;
     if(T.tu){//不是空矩陣
        int col,t,q;//col 列號 t 元素位置 
	    int * num=(int *)malloc((M.nu+1)*sizeof(int));//0不用 所以多分配一個
		if(!num) //儲存分配失敗
			exit(OVERFLOW);
	    for(col=1;col<=M.nu;col++)
	        num[col]=0; //num賦0
	    for(t=1;t<=M.tu;t++)
	        num[M.data[t].j]++;
        int * cpot=(int *)malloc((M.nu+1)*sizeof(int));
        if(!cpot)//儲存分配失敗
			exit(OVERFLOW);
        cpot[1]=1;//M中第一個列標為1的非零元素必在T的第一位置
        for(col=2;col<=M.nu;col++)
            cpot[col]=cpot[col-1]+num[col-1];
		for(t=1;t<=M.tu;t++) {
		    col=M.data[t].j;//求列號
		    q=cpot[col];//求插入位置
		    T.data[q].e=M.data[t].e;
		    T.data[q].j=M.data[t].i;
		    T.data[q].i=M.data[t].j;
            cpot[col]++;//插入位置自增
		}
		free(num);//銷燬清空
		free(cpot);
		num=NULL;
		cpot=NULL;
	 }
     return OK;
}

複雜度:O(M.nu+M.tu) 空間多了num[M.nu+1]  cpot[M.nu+1] ,O(M.nu)

給定下標 求元素的指定值:

ElemType Value(RLSMatrix M,int r,int c){
	//給定下標 求元素的指定值
   int k=M.rpos[r];   //r行第一個非零元素的位置
   while(M.data[k].j<c&&M.data[k].i==r)
        k++;
   if(M.data[k].i==r&&M.data[k].j==c)
        return M.data[k].e;
   return 0;
}

求稀疏矩陣乘積Q=M*N。

Status MultSMatrix(RLSMatrix M,RLSMatrix N,RLSMatrix &Q){
	//求稀疏矩陣乘積Q=M*N
   if(M.nu!=N.mu) //M列數不等於N的行數
      return ERROR;
   Q.mu=M.mu;//Q初始化
   Q.nu=N.nu;
   Q.tu=0;
   int *ctrmp=(int *)malloc((N.nu+1)*sizeof(int)); 
   if(!ctrmp) //儲存分配失敗
	  exit(OVERFLOW);
   if(M.tu*N.tu!=0) {//Q是非零矩陣
	 int arow,blow,tp,p,t,q,i,ccol,j;
     for(arow=1;arow<=M.mu;arow++){//逐行求積
        for(i=1;i<=N.nu;i++)
		    ctrmp[i]=0;//Q中各行累加器歸零
	    Q.rpos[arow]=Q.tu+1; //Q的rpos賦值
		for(j=arow;j<M.mu;j++){
			if(M.rpos[j+1]){  //給tp賦值給M中下一個有非零元的位置 找到就跳出迴圈
		      tp=M.rpos[j+1];   
		      break;
			} 
		}
	    if(j==M.mu) //沒找到就賦給元素非零元素總數加一
		   tp=M.tu+1;
        for(p=M.rpos[arow];p<tp;p++){//對當前行中的每一個非零元
			if(p){//如果非零元存在
			    blow=M.data[p].j;//找到對應元在N中的行號
				for(j=blow;j<N.mu;j++){//給t賦值N中下一個非零元的位置
		            if(N.rpos[j+1]) { 
		               t=N.rpos[j+1];   
		               break;
					}
				}  
	            if(j==N.mu)
		            t=N.tu+1;
				for(q=N.rpos[blow];q<t;q++){//遍歷N中blow行中每一個非零元
                    if(q){//如果非零元存在
			            ccol=N.data[q].j;//乘積元素在Q中列號
			            ctrmp[ccol]+=M.data[p].e*N.data[q].e;//計算當前行的積並存入ctrmp
					} 
			        else//否則跳出迴圈
                       break;
				}//for q			
			}//建立稀疏矩陣M
			else //否則跳出迴圈
				break;		    
		}//for p
        for(ccol=1;ccol<=N.nu;ccol++)//遍歷ctrmp 如果不為零就壓縮儲存中Q.data
           if(ctrmp[ccol]){
              if(++Q.tu>MAXSIZE) return ERROR; //超出最大容量
		      Q.data[Q.tu].e=ctrmp[ccol];
		      Q.data[Q.tu].i=arow;
              Q.data[Q.tu].j=ccol;
		   }
	 }//for arow
   }//if
   return OK;
}