稀疏矩陣——三元組順序表


稀疏矩陣

假設m*n的矩陣中,有t的非零元,令s=t/m * n,當,s<=0.05時,稱此矩陣為稀疏矩陣,簡單理解就是非零元特別少的矩陣

//一般矩陣a
    1 2 3
 a= 4 5 6
    7 8 9
//稀疏矩陣s
    0 0 0 0 0 
    0 2 0 0 5
 s= 0 0 3 0 0
    0 0 0 0 4

矩陣的轉置

一個m * n的矩陣轉置后變為 n * m的矩陣

//3*2的矩陣-轉置前
1 2 
4 5
7 8
//轉置后變為2*3
1 4 7
2 5 8

轉置后的矩陣每個元素的下表與原來的下表剛好相反,例如上面4轉置前的下標為(2,1),轉置后變為(1,2);

矩陣壓縮存儲-三元組順序表

之所以引入三元組順序表,是因為,對於稀疏矩陣而言,用傳統的存儲方法會造成存儲空間的浪費

   0 12  9  0  0  0  0
   0  0  0  0  0  0  0
  -3  0  0  0  0 14  0
M= 0  0 24  0  0  0  0
   0 18  0  0  0  0  0
  15  0  0 -7  0  0  0

//上面矩陣用三元組表示
i  j  v      
1  2  12
1  3  9
3  1  -3
3  6  14
4  3  24 
5  2  18
6  1  15
6  4  -7
typedef  struct
{
    int i,j;     //行坐標、列坐標
    ElemType e;  //元素
}Triple;

typedef struct
{
    Triple date[MAXSIZE+1];  //0不存儲元素
    int mu,nu,tu;      //行數、列數、非零元個數
}TSMatrix;

稀疏矩陣的轉置

傳統方法的轉置算法時遍歷矩陣的每一項,交換其下標值即可

for(col=1;col<=nu;col++)
{
  for(row=1;row<=mu;row++)
  {
    T[col][row]=M[row][col]
  }
}

//時間復雜度 : O(nu*mu)

利用三元組順序表進行存儲的稀疏矩陣要想實現轉置顯然不能用上面的算法,下面介紹兩種方法:

第一種:以列序為主序的轉置

//置換前       存儲位置
i  j  v   
1  2  12  ->  M.date[1]
1  3  9   ->  M.date[2]
3  1  -3  ->  M.date[3]
3  6  14  ->  M.date[4]
4  3  24  ->  M.date[5]
5  2  18  ->  M.date[6]
6  1  15  ->  M.date[7]
6  4  -7  ->  M.date[8]
//置換后       存儲位置
i  j  v   
1  3  -3  ->  T.date[1]
1  6  15  ->  T.date[2]
2  1  12  ->  T.date[3]
2  5  18  ->  T.date[4]
3  1  9   ->  T.date[5]
3  4  24  ->  T.date[6]
4  6  -7  ->  T.date[7]
6  3  14  ->  T.date[8]
void TransposeSMatrix(TSMatrix *T1,TSMatrix *T2)
{
    T2->mu=T1->nu;T2->nu=T1->mu;T2->tu=T1->tu;
    if(T1->tu)
    {
        int q=1,col,p;
        for(col=1;col<=T1->nu;col++)  //矩陣列循環
        {
            for(p=1;p<=T1->tu;p++)    //遍歷所有元素
            {
                if(T1->date[p].j==col)  //當元素在col列時
                {
                    T2->date[q].i=T1->date[p].j;
                    T2->date[q].j=T1->date[p].i;
                    T2->date[q].e=T1->date[p].e;
                    q++;
                }
            }
        }
    }
}
//上述代碼,當矩陣運算為滿時,即tu=mu*nu,其時間復雜度為O(nu*nu*mu)
//這種情況與經典算法相比,雖節省了存儲空間,但是效率較低

第二種:快速轉置

第一種算法是通過遍歷所有元素的下標,從而確定其在轉置后數組中的位置,快速轉置的思想就是,預先確定每一列第一個非零元在對應轉置后的數組date中的位置;因此需要兩個輔助數組

num[]:用來存放每一列的非零元個數

cpot[]:存放第一個非零元在轉置后數組date中的位置

num[]數組的值很好求,只需要遍歷一次所有元素即可

for(t=1;t<=T1->tu;t++)
    ++num[T1->date[t].j];

對於cpot[],有一個規律

     col  1 2 3 4 5 6 7
num[col]  2 2 2 1 0 1 0
cpot[col] 1 3 5 7 8 8 9
       
//規律
copt[1]=1
copt[col]=copt[col-1]+num[col-1]

代碼:

void FastTransposeSMatrix(TSMatrix *T1,TSMatrix *T2)
{
    int num[T1->nu],cpot[T1->nu];
    int col,p,q,t;
    T2->mu=T1->nu;T2->nu=T1->mu;T2->tu=T1->tu;
    if(T1->tu)
    {
        //初始化每列非零元個數為0
        for(col=1;col<=T1->nu;col++)
        {
            num[col]=0;
        }
        //求每列非零元個數
        for(t=1;t<=T1->tu;t++)
        {
            ++num[T1->date[t].j];
        }
        //求每列第一個非零元轉置后的位置
        cpot[1]=1;
        for(col=2;col<=T1->nu;col++)
        {
            cpot[col]=num[col-1]+cpot[col-1];
        }
        //遍歷所有元素
        for(p=1;p<=T1->tu;p++)
        {
            col=T1->date[p].j;  //獲取列坐標
            q=cpot[col];        //獲取新位置
            T2->date[q].i=T1->date[p].j;
            T2->date[q].j=T1->date[p].i;
            T2->date[q].e=T1->date[p].e;
            ++cpot[col];   //之所以這個地方要++,因為每列非零元可能不止一個
        }  
    }
}

完整代碼:

#include <stdio.h>
#include <stdlib.h>

#define  MAXSIZE 12500  //非零元個數的最大值

typedef int ElemType;

typedef  struct
{
    int i,j;
    ElemType e;
}Triple;

typedef struct
{
    Triple date[MAXSIZE+1];
    int mu,nu,tu;
}TSMatrix;
//輸入元素
void Insert(TSMatrix *T)
{
    printf("請依次輸入行數i、列數j、非零元個數sum:\n");
    int sum ;
    scanf("%d%d%d",&T->mu,&T->nu,&sum);
    T->tu=sum;
    int x,y,num;
    printf("請依次輸入矩陣非零元的行坐標i、列坐標j、元素值x:\n");
    printf("i j v\n");
    for(int i=1 ;i<=sum;i++)
    {
        scanf("%d%d%d",&x,&y,&num);
        T->date[i].i=x;
        T->date[i].j=y;
        T->date[i].e=num;
    }
}
//第一種轉置方法
void TransposeSMatrix(TSMatrix *T1,TSMatrix *T2)
{
    T2->mu=T1->nu;T2->nu=T1->mu;T2->tu=T1->tu;
    if(T1->tu)
    {
        int q=1,col,p;
        for(col=1;col<=T1->nu;col++)
        {
            for(p=1;p<=T1->tu;p++)
            {
                if(T1->date[p].j==col)
                {
                    T2->date[q].i=T1->date[p].j;
                    T2->date[q].j=T1->date[p].i;
                    T2->date[q].e=T1->date[p].e;
                    q++;
                }
            }
        }
    }
}
//輸出矩陣非零元
void Show(TSMatrix *T)
{
    printf("轉置后的矩陣:\n");
    printf("i j v\n");
    for(int i=1;i<=T->tu;i++)
    {
        printf("%d %d %d\n",T->date[i].i,T->date[i].j,T->date[i].e);
    }
}
//快速轉置
void FastTransposeSMatrix(TSMatrix *T1,TSMatrix *T2)
{
    int num[T1->nu],cpot[T1->nu];
    int col,p,q,t;
    T2->mu=T1->nu;T2->nu=T1->mu;T2->tu=T1->tu;
    if(T1->tu)
    {
        //初始化每列非零元個數為0
        for(col=1;col<=T1->nu;col++)
        {
            num[col]=0;
        }
        //求每列非零元個數
        for(t=1;t<=T1->tu;t++)
        {
            ++num[T1->date[t].j];
        }
        
        cpot[1]=1;
        for(col=2;col<=T1->nu;col++)
        {
            cpot[col]=num[col-1]+cpot[col-1];
        }
        
        for(p=1;p<=T1->tu;p++)
        {
            col=T1->date[p].j;
            q=cpot[col];
            T2->date[q].i=T1->date[p].j;
            T2->date[q].j=T1->date[p].i;
            T2->date[q].e=T1->date[p].e;
            ++cpot[col];
        }
        
    }
}

int main()
{
    TSMatrix T,T1,*q,*p;
    p=&T;q=&T1;
    Insert(p);
    //測試第一種轉置方法
    TransposeSMatrix(p, q);  
    Show(q);
    //測試快速轉置
    FastTransposeSMatrix(p, q);
    Show(q);
}
/*  測試
請依次輸入行數i、列數j、非零元個數sum:
6 7 8
請依次輸入矩陣非零元的行坐標i、列坐標j、元素值x:
i j v
1 2 12
1 3 9
3 1 -3
3 6 14
4 3 24
5 2 18
6 1 15
6 4 -7
轉置后的矩陣:
i j v
1 3 -3
1 6 15
2 1 12
2 5 18
3 1 9
3 4 24
4 6 -7
6 3 14
轉置后的矩陣:
i j v
1 3 -3
1 6 15
2 1 12
2 5 18
3 1 9
3 4 24
4 6 -7
6 3 14
Program ended with exit code: 0
*/

我不生產代碼,我只是代碼的搬運工


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM