稀疏矩阵——三元组顺序表

稀疏矩阵

假设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
*/

我不生产代码,我只是代码的搬运工

posted @ 2019-10-28 19:41  翁德彪  阅读(6353)  评论(0编辑  收藏  举报