資料結構-稀疏矩陣-靜態分配的三元組順序儲存
阿新 • • 發佈:2018-12-14
假設在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;
}