快速转置的原理是:如果能预先确定矩阵M中每一列(即T中每一行)的第一个非零元在b.data中(上面那图是b.data)恰当位置。那么在对a.data中的三元组一次做转置时,便可直接放到b.data中恰当的位置上去。

设两个向量:num和cpot

num[col]表示矩阵M中第col列中的非零元素个数。

cpot[col]指M中第col列的第一个非零元在b.data中的恰当位置。

有下面两个公式:

cpot[0]=0;

cpot[col]=copt[col-1]+num[col-1] 1<=col<a.nu
对于一个三元表,行为i,列为j,值为v。需将其i与j的值对调才能得到新的三元表,但是如果直接进行转换,得到的新的三元表的顺序是混乱的,不符合三元表的规则。所以,课本首先介绍了一个用扫描来转置的算法(这个算法比较容易,在这里我就不说了),但是这个转置算法的时间复杂度太高,于是就有了接下来的快速转置算法。

 

         要你对一个三元表进行步骤最少的转置,你可能会想,如果知道三元表中每一项在转置后的新的三元表中的位置,然后直接放进去,岂不是极大的缩小了时间复杂度?没错!快速转置法正是基于这种思想而设计的。

转置的时候直接从第一项读起,读取其j值,比如课本中a.data这个三元表的第一项的j值为2,因为j=2占据第3、4位,所以应该从第三位开始放,接下来如果读取的某一项的j值也是2,就放在第4位。因为j=2的项只有两个,所以第5位绝对不会被j=2的项占据,第5、6项本来就是留给j=3的。再比如当读到j=6的那项时,第8位是留给它的,就可以直接放进第8位了。这样,读取每一项,都能在三元表中找到相应的位置,这就是稀疏矩阵快速转置的原理。

#include<stdio.h>
#define M 6//行数
#define N 6//列数
#define Maxsize 8

typedef struct
{
    int r;//行号
    int c;//列号
    int d;//元素值
}TupNode;//三元组类型


typedef struct
{
    int rows;//行数
    int cols;//列数
    int num;//非零元个数
    TupNode data[Maxsize];//非零元素的行列以及元素值的三元组信息
}TSMatrix;//三元表类,一个三元表相当于一个稀疏矩阵


void CreateMat(TSMatrix &t,int a[M][N])//创建一个三元表,从a数组里面获取数据
{
  int i,j;
   t.rows=M;t.cols=N;t.num=0;//初始化一张三元表
        for(i=0;i<M;i++)//逐行扫描
        {
            for(j=0;j<N;j++)//逐列扫描
            {
                if(a[i][j]!=0)
                {
                    t.data[t.num].r=i;//更新最大行数
                    t.data[t.num].c=j;//更新最大列数
                    t.data[t.num].d=a[i][j];//插入元素值
                    t.num++;//非零元素个数+1,因为是从0开始,所以只要执行if语句,个数就+1;
                }
            }
        }
}

void DispMat(TSMatrix  t)//输出一个稀疏矩阵的三元表
{
    int k;
    if(t.num<=0) return;//没有非零元素时直接返回
    printf("\t%d\t%d\t%d\n",t.rows,t.cols,t.num);//表头:行数,列数,非零元素数
    printf("\t---------------------------------\n");
    for(k=0;k<t.num;k++)//输出每一个非零元素的三元信息
    {
        printf("\t%d\t%d\t%d\n",t.data[k].r,t.data[k].c,t.data[k].d);
    }
}
void show(TSMatrix t)//把矩阵直观的显示出来
{
    int a[M][N];
    int m,n,k;
    for (m=0;m<M;m++)
    {

        for(n=0;n<N;n++)
        {
            a[m][n]=0;
        }
            
    }
    for(k=0;k<t.num;k++)
    {
        n=t.data[k].c;
        m=t.data[k].r;
        a[m][n]=t.data[k].d;
    }

    for (m=0;m<M;m++)
    {

        for(n=0;n<N;n++)
        {
            printf("%3d",a[m][n]);
        }
        printf("\n");
            
    }

}


void FastTranTat(TSMatrix m,TSMatrix &t)//快速转置函数
{
int numm[N];//记录每一列的非零元素的个数,一共有N列故数组长度为n
int cpot[Maxsize];//用于记录三元表中某个列数的第一个非零项在新三元表中的位置
t.cols=m.rows;  t.rows=m.cols;  t.num=m.num;//初始化矩阵t
if(t.num)
{
    for(int i=0;i<m.cols;i++)
    {
        numm[i]=0;//初始化数组,先全部置为0;
    }
    for(int i=0;i<m.num;i++)
    {
        ++numm[m.data[i].c];//求m中每一列含非零元的个数,预留出在新三元表中的位置
     }
    cpot[0]=0;//原三元表中第一例的首非零元在新三元表的第一列
    for(int i=1;i<m.cols;i++)
    {
        cpot[i]=cpot[i-1]+numm[i-1];//求除了第一列其它每一列的第一个非零元在新·三元表中的位置
    }
    for(int p=0;p<m.num;p++)//遍历原三元表的每一项
    {
        t.data[cpot[m.data[p].c]].r=m.data[p].c;
        /*
        这一句有点绕,其实可以分成好几层临时变量来写,但是这样层层嵌套
        更加直观一点。它其实
        左边就是 旧三元表 某一个元素的列数里面的第一个非零
        元素 在 新三元表中的位置,然后这个位置的行号。

        右边是 旧三元表中某一元素的列号

        然后,把右边赋给左边,也就是旧列号变新行号

        由于每一个旧三元表中的元素都能得到相应的操作,所以遍历一次即可
        一个萝卜一个坑的填进新三元表
        */
       t.data[cpot[m.data[p].c]].c=m.data[p].r;//同理,不过是旧行号变新列号
        t.data[cpot[m.data[p].c]].d=m.data[p].d;//元素值也要赋过来
        cpot[m.data[p].c]++;//这句的意思是旧三元表某列首非零元在
                             //新三元表中的位置经过以上处理要向下
                             //移动一个位置,在新三元表里看,这个操作就是
                             //某一行号的元素处理完了,然后往下移动
                             //一个位置,下次如果还要处理这个行号的元素
                             //就直接用移动好的这个位置。因为每一行号的元素位置
                             //都被预留好了,所以不用担心溢出。

        
    }
}

}
int main()
{
    int a[6][6]={{0,12,9,0,0,0},{0,0,0,0,0,0},{-3,0,0,0,0,0},{0,0,0,24,0,0}
                   , {0,18,0,0,0,0},{15,0,0,-7,0,14}};
    
    TSMatrix t,tb;
    CreateMat(t,a);
    printf("原矩阵三元表:\n");
    DispMat(t);
    printf("矩阵为\n\n");
    show(t);
    printf("转置后三元表\n");
    FastTranTat(t,tb);

    DispMat(tb);
    printf("矩阵为\n\n");
    show(tb);

}


0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注