28、数据结构笔记之二十八数组之矩阵

“在命运的颠沛中,最可以看出人们的气节。 -- 莎士比亚”

上篇咱们看了数组的一些定义和概念,接下去我们来看下数据和矩阵。

 

 

1.  特殊矩阵

二维数组可以来存储矩阵元素。有的程序设计语言中还供了各种矩阵运算,给用户使用带来很大的方便。然而,在数值分析中经常出现一些高阶矩阵,在这些高阶矩阵中有许多值相同的元素或者是零元素,为了节省存储空间对这类矩阵采用多个值  相同的元素只分配一个存储空间,有时零元素不存储的存储策略,称为矩阵的压缩存储。

 

如果值相同的元素或者零元素在矩阵中的分布有一定规律,称此类矩阵为特殊矩阵,反之称为稀疏矩阵

 

1.1      对称矩阵

若一个n阶矩阵的主满足aij=aj i  1≤i,j≤n,则称该矩阵为n阶对称矩阵。

由于对称矩阵有上述性质,可以为每一对对称元分配一个存储空间,这样就将n2个元的存储空间压缩成n(n+1)/2个单元的存储空间,以行序为主序存储对称矩阵的下三角(包括对角线)的元素。

 

1.2      三角矩阵

三角矩阵分上三角矩阵和下三角矩阵两种。上三角矩阵的对角线左下方的系数全部为零,下三角矩阵的对角线右上方的系数全部为零。三角矩阵可以看做是一般方阵的一种简化情形。

 

1.3      对角矩阵

对角矩阵(diagonalmatrix)是一个主对角线之外的元素皆为 0 的矩阵。对角线上的元素可以为 0或其他值。

 

 

2.  稀疏矩阵

矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素的分布没有规律,则称该矩阵为稀疏矩阵(sparsematrix);与之相区别的是,如果非零元素的分布存在规律(如上三角矩阵、下三角矩阵、对称矩阵),则称该矩阵为特殊矩阵。

人们无法给出确切的定义,它只是一个凭人们的直觉来了解的概念。假设在m×n的矩阵中,有t个非零元素,若t远远小于矩阵元素的总数(即t<<m×n),则称A为稀疏矩阵。

精确点,设在矩阵A中,有t个非零元素。令,称δ为矩阵的稀疏因子。通常认为δ≤0.05时称之为稀疏矩阵。

 

2.1      稀疏矩阵存储

在存储稀疏矩阵时,为了节省存储单元,很自然地想到使用压缩存储方法。

2.1.1          三元组

但由于非零元素的分布一般是没有规律的,因此在存储非零元素的同时,还必须同时记下它所在的行和列的位置(i,j)。反之,一个三元组(i,j,aij)唯一确定了矩阵A的一个非零元。因此,稀疏矩阵可由表示非零元的三元组及其行列数唯一确定。比如(1,2,3)和(3,2,1)就是两个不同的有序三元组.

2.1.2          行逻辑链接顺序表

方法在三元组顺序表的基础上,增加一个数组存储矩阵中每行第一个非0元素出现的位置。

2.1.3          十字链表

十字链表表示稀疏矩阵比较复杂。具体查看《18、数据结构笔记之十八链表实现稀疏矩阵》

 

 

3.  特殊矩阵压缩存储

主要考虑的是对称矩阵,三角矩阵及三对角矩阵的一些操作。

3.1      定义

定义了如下全局变量

//矩阵的压缩存储

int TA[MaxN],TC[MaxN];

int n;

int A[MaxN][MaxN],B[MaxN][MaxN]; 

 

 

3.2      Value

输入要操作的对称矩阵阶数。

输入每个矩阵中的每个数。只接受一半的数字输入即可。

然后通过循环实现另一半的数字录入。

然后输出矩阵。

最后将矩阵输入到一个数组中,并进行输出。

void value()

{

  inti,j,k;

  printf("请输入要操作的对称矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入对称矩阵:");

    for(i=1;i<=n;i++)

        for(j=i;j<=n;j++)

           scanf("%d",&A[i][j]);

                     for(i=1;i<=n;i++)

                     {

                                for(j=1;j<=n;j++)

                                {

                                          if(i>j)  A[i][j]=A[j][i];

                                          else  A[i][j]=A[i][j];

                                }

                     }

                     printf("输出对称矩阵:\n");

                     for(i=1;i<=n;i++)

                     {

                                for(j=1;j<=n;j++)

                                          printf("%-4d",A[i][j]);

                                printf("\n");

                     }

                    

                     k=0;

                     for(i=1;i<=n;i++)          

                                for(j=1;j<=n;j++)

                                          TA[k++]=A[i][j];

                                printf("压缩后的对称矩阵:\n");

                                for(k=0;k<=n*n-1;k++)

                                          printf("%-4d",TA[k]);

                                printf("\n");

}

 

 

 

3.3      Sfdg

输入上三角矩阵的阶数,然后输入数字,只取行数大于列数的数字。其他值为0.

然后进行输出,最后复制给数组进行输出。

void sfdg()

{

  inti,j,k;

  printf("请输入要操作的上三角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入上三角矩阵:");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

             {

                       if(i>j)  A[i][j]=0;

                       else  scanf("%d",&A[i][j]);

             }

  }

  printf("输出上三角矩阵:\n");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

                       printf("%-4d",A[i][j]);

             printf("\n");

  }

 

  k=0;

  for(i=1;i<=n;i++)      

             for(j=1;j<=n;j++)

                       TA[k++]=A[i][j];

             printf("压缩后的上三角矩阵:\n");

             for(k=0;k<=n*n-1;k++)

                       printf("%-4d",TA[k]);

             printf("\n");

}

 

3.4      Sfvd

下三角同理。

3.5      Store

输入三对角矩阵的阶数。

存储3条主对角线上的数字,然后保存输出。

void store()

{

  inti,j,k;

  printf("请输入要操作的三对角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入三对角矩阵:");

  for(i=1;i<=n;i++)

  {

             if(i==1)

             {

                       for(j=1;j<=2;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i>1&&i<n)

             {

                       for(j=i-1;j<=i+1;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i=n)

             {

                       for(j=i-1;j<=i;j++)

                                  scanf("%d",&A[i][j]);

             }

  }

  printf("输出三对角矩阵:\n");

    for(i=1;i<=n;i++)

           {

        for(j=1;j<=n;j++)

                                printf("%-4d",A[i][j]);

                     printf("\n");

           }

          

           k=0;

           for(i=1;i<=n;i++)          

                     for(j=1;j<=n;j++)

                                if(A[i][j]!=0) 

                                          TA[k++]=A[i][j];

                                printf("压缩后的三对角矩阵:\n");

                                for(k=0;k<=n*n-1;k++)

                                          printf("%-4d",TA[k]);

}

3.6      Add

输入三对角矩阵阶数,先输入A

然后输入矩阵B。

 

 

 

 

3.7      menu_list

只是个菜单,详见源码部分。

3.8      Main函数

Main函数实现主菜单函数的调用,然后根据输入数字调用不同的函数。

           如下图1:

 

 

3.9      源码

#include<stdio.h>

#include<stdlib.h>

#defineMaxN100

 

//矩阵的压缩存储

int TA[MaxN],TC[MaxN];

int n;

int A[MaxN][MaxN],B[MaxN][MaxN]; 

void value()

{

  inti,j,k;

  printf("请输入要操作的对称矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入对称矩阵:");

    for(i=1;i<=n;i++)

        for(j=i;j<=n;j++)

           scanf("%d",&A[i][j]);

                     for(i=1;i<=n;i++)

                     {

                                for(j=1;j<=n;j++)

                                {

                                          if(i>j)  A[i][j]=A[j][i];

                                          else  A[i][j]=A[i][j];

                                }

                     }

                     printf("输出对称矩阵:\n");

                     for(i=1;i<=n;i++)

                     {

                                for(j=1;j<=n;j++)

                                          printf("%-4d",A[i][j]);

                                printf("\n");

                     }

                    

                     k=0;

                     for(i=1;i<=n;i++)          

                                for(j=1;j<=n;j++)

                                          TA[k++]=A[i][j];

                                printf("压缩后的对称矩阵:\n");

                                for(k=0;k<=n*n-1;k++)

                                          printf("%-4d",TA[k]);

                                printf("\n");

}

 

void sfdg()

{

  inti,j,k;

  printf("请输入要操作的上三角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入上三角矩阵:");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

             {

                       if(i>j)  A[i][j]=0;

                       else  scanf("%d",&A[i][j]);

             }

  }

  printf("输出上三角矩阵:\n");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

                       printf("%-4d",A[i][j]);

             printf("\n");

  }

 

  k=0;

  for(i=1;i<=n;i++)      

             for(j=1;j<=n;j++)

                       TA[k++]=A[i][j];

             printf("压缩后的上三角矩阵:\n");

             for(k=0;k<=n*n-1;k++)

                       printf("%-4d",TA[k]);

             printf("\n");

}

 

void sfvd()

{

  inti,j,k;

  printf("请输入要操作的下三角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入下三角矩阵:");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

             {

                       if(i>=j)  scanf("%d",&A[i][j]);

                       else  A[i][j]=0;

             }

  }

  printf("输出下三角矩阵:\n");

  for(i=1;i<=n;i++)

  {

             for(j=1;j<=n;j++)

                       printf("%-4d",A[i][j]);

             printf("\n");

  }

 

  k=0;

  for(i=1;i<=n;i++)

             for(j=1;j<=n;j++)

                       TA[k++]=A[i][j];

             printf("压缩后的下三角矩阵:\n");

             for(k=0;k<=n*n-1;k++)

                       printf("%-4d",TA[k]);

             printf("\n");

}

 

void store()

{

  inti,j,k;

  printf("请输入要操作的三对角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入三对角矩阵:");

  for(i=1;i<=n;i++)

  {

             if(i==1)

             {

                       for(j=1;j<=2;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i>1&&i<n)

             {

                       for(j=i-1;j<=i+1;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i=n)

             {

                       for(j=i-1;j<=i;j++)

                                  scanf("%d",&A[i][j]);

             }

  }

  printf("输出三对角矩阵:\n");

    for(i=1;i<=n;i++)

           {

        for(j=1;j<=n;j++)

                                printf("%-4d",A[i][j]);

                     printf("\n");

           }

          

           k=0;

           for(i=1;i<=n;i++)          

                     for(j=1;j<=n;j++)

                                if(A[i][j]!=0) 

                                          TA[k++]=A[i][j];

                                printf("压缩后的三对角矩阵:\n");

                                for(k=0;k<=n*n-1;k++)

                                          printf("%-4d",TA[k]);

}

 

void add()

{

  inti,j,k;

  printf("请输入要操作的三对角矩阵的阶数:");

  scanf("%d",&n);

 

  printf("输入三对角矩阵A:");

  for(i=1;i<=n;i++)

  {

             if(i==1)

             {

                       for(j=1;j<=2;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i>1&&i<n)

             {

                       for(j=i-1;j<=i+1;j++)

                                  scanf("%d",&A[i][j]);

             }

             elseif(i=n)

             {

                       for(j=i-1;j<=i;j++)

                                  scanf("%d",&A[i][j]);

             }

  }

  printf("输出三对角矩阵A:\n");

    for(i=1;i<=n;i++)

           {

        for(j=1;j<=n;j++)

                                printf("%-4d",A[i][j]);

                     printf("\n");

           }

          

           k=0;

           for(i=1;i<=n;i++)          

                     for(j=1;j<=n;j++)

                                if(A[i][j]!=0) 

                                          TA[k++]=A[i][j];

                                printf("压缩后的三对角矩阵A:\n");

                                for(k=0;k<=n*n-1;k++)

                                          printf("%-4d",TA[k]);

                                printf("\n");

  printf("输入三对角矩阵B:");

  for(i=1;i<=n;i++)

  {

             if(i==1)

             {

                       for(j=1;j<=2;j++)

                                  scanf("%d",&B[i][j]);

             }

             elseif(i>1&&i<n)

             {

                       for(j=i-1;j<=i+1;j++)

                                  scanf("%d",&B[i][j]);

             }

             elseif(i=n)

             {

                       for(j=i-1;j<=i;j++)

                                  scanf("%d",&B[i][j]);

             }

  }

  printf("输出三对角矩阵B:\n");

    for(i=1;i<=n;i++)

           {

        for(j=1;j<=n;j++)

                                printf("%-4d",B[i][j]);

                     printf("\n");

           }

 

  k=0;

  for(i=1;i<=n;i++)      

      for(j=1;j<=n;j++)

      if(B[i][j]!=0) 

     TC[k++]=B[i][j];

      printf("压缩后的三对角矩阵B:\n");

      for(k=0;k<=n*n-1;k++)

               printf("%-4d",TC[k]);

 

           intTB[MaxN];

           for(k=0;k<=n*n-1;k++)

                     TB[k]=TA[k]+TC[k];

           printf("相加后的压缩三对角矩阵:\n");

           for(k=0;k<=n*n-1;k++)

                     printf("%-4d",TB[k]);

           printf("\n");

}

 

//主菜单

int menu_list()

{

            int c;    

     printf("\n\n**************************特殊矩阵的压缩存储**************************\n\n");

            printf("               1.对称矩阵的压缩存储\n");

            printf("               2.上三角矩阵的压缩存储\n");

            printf("               3.下三角矩阵的压缩存储\n");

            printf("               4.三对角矩阵的压缩存储\n");

            printf("               5.三对角矩阵的加法运算\n");

            printf("               6.退出系统\n");

            printf("               请输入(1-6)[ ]\b\b");

                      

            while(1)

            {

                      scanf("%d",&c);

                      if(c<1||c>6)

                          printf("输入错误,请重新输入:");

         else

                          break;

            }

            return c;

}

 

//主函数

void main()

{

           while(1)

           {        

               switch(menu_list())

                     {

                        case 1:                      

                            value();

                                   break;

                        case 2:

                                sfdg();

                                   break;

                        case 3:

                                   sfvd();

              break;

                        case 4:

                                   store();

                                   break;

                        case 5:

                            add();

                                   break;

                        case 6:

                                   printf("   程序结束,谢谢您的使用!\n\n");

                                   exit(0);

                     }

           }

}

 

 

 

 

 

 

4.  稀疏矩阵压缩存储

 

4.1      定义

//定义结构体 

#defineMAXSIZE100 

typedefstruct 

    inti,j; 

    intdat; 

}Triple

//矩阵的顺序链表 

typedefstruct 

    Tripledata[MAXSIZE+1]; 

    intmu,nu,tu; 

}TSMatrix

 

 

 

4.2      FastConvert

输入两个参数M,和T的指针。

通过M.nu创建内存空间,分别给num和cpot。

将M的值复制给T矩阵。

Num是每列非零元素个数的数组。

Cpot是每列第一个非零元素位置 

 

//稀疏矩阵存储 

int FastConvert(TSMatrixM,TSMatrix *T

    int*num,*cpot,i,p,col; 

    //分别为一维指针分配内存 

    num=(int*)malloc(sizeof(int)*M.nu); 

    cpot=(int*)malloc(sizeof(int)*M.nu); 

 

    T->mu=M.nu;T->nu=M.mu;T->tu=M.tu; 

 

    if(T->tu) 

    {  

        //初始化每一列非零元素个数为零 

         for(i=0;i<M.nu;i++)  

            num[i]=0; 

         //初始化数组将其置为每一列的非零元素个数 

         for(i=0;i<M.tu;i++) 

            ++num[M.data[i].j]; 

         //cpot数组表示每一列第一个非零元素位置 

        cpot[0]=0; 

         for(i=1;i<M.nu;i++) 

            cpot[i]=cpot[i-1]+num[i-1]; 

         //进行矩阵值的交换 

         for(i=0;i<M.tu;i++) 

        { 

             col=M.data[i].j;  p=cpot[col]; 

              T->data[p].i=M.data[i].j; 

              T->data[p].j=M.data[i].i; 

              T->data[p].dat=M.data[i].dat; 

             ++cpot[col]; 

        } 

    } 

    return1; 

 

4.3      Main

定义变量T,M。定义矩阵指针为NULL。输入行和列数量。

然后创建行数乘以列数的的整型指针数量。然后输入数据。

如果为0,则不增加变量tu和count的值。然后调用FastConvert函数。

运行如下图2 所示:

可以看到最后将一个稀疏矩阵成功的转化成为了一个数组。

不过需要注意的是:

稀疏矩阵压缩存储后,必会失去随机存取功能。
稀疏矩阵在采用压缩存储后将会失去随机存储的功能。因为在这种矩阵中,非零元素的分布是没有规律的,为了压缩存储,就将每一个非零元素的值和它所在的行、列号做为一个结点存放在一起,这样的结点组成的线性表中叫三元组表,它已不是简单的向量,所以无法用下标直接存取矩阵中的元素。

 

 

int main(intargc,char* argv[]) 

    TSMatrixT,M; 

    intcol,row,i,j,count=0; 

    int**p=NULL

 

    printf("请输入矩阵的行列row col\n"); 

    scanf("%d%d",&row,&col); 

    //为未知的二维指针分配内存 

    p=(int**)malloc(sizeof(int*)*row); 

    for(i=0;i<row;i++) 

        p[i]=(int*)malloc(sizeof(int)*col); 

   

 

    printf("请输入数据!\n"); 

 

   M.mu=row;  M.nu=col;  M.tu=0; 

    for(i=0;i<row;i++) 

        for(j=0;j<col;j++) 

        { 

           scanf("%d",&p[i][j]); 

            if(p[i][j]!=0) 

           { 

               M.data[count].dat=p[i][j]; 

               M.data[count].i=i; 

                M.data[count].j=j; 

               M.tu++; 

               count++; 

           } 

 

        } 

   count=0; 

   FastConvert(M,&T); 

    for(i=0;i<T.tu;i++) 

    { 

       printf("%d ",T.data[count].dat); 

       count++; 

    } 

   free(p); 

    return0; 

4.4      源码

#include<stdio.h> 

#include<stdlib.h> 

 

//定义结构体 

#defineMAXSIZE100 

typedefstruct 

    inti,j; 

    intdat; 

}Triple

//矩阵的顺序链表 

typedefstruct 

    Tripledata[MAXSIZE+1]; 

    intmu,nu,tu; 

}TSMatrix

 

int FastConvert(TSMatrixM,TSMatrix *T); 

 

int main(intargc,char* argv[]) 

    TSMatrixT,M; 

    intcol,row,i,j,count=0; 

    int**p=NULL

 

    printf("请输入矩阵的行列row col\n"); 

    scanf("%d%d",&row,&col); 

    //为未知的二维指针分配内存 

    p=(int**)malloc(sizeof(int*)*row); 

    for(i=0;i<row;i++) 

        p[i]=(int*)malloc(sizeof(int)*col); 

   

 

    printf("请输入数据!\n"); 

 

   M.mu=row;  M.nu=col;  M.tu=0; 

    for(i=0;i<row;i++) 

        for(j=0;j<col;j++) 

        { 

           scanf("%d",&p[i][j]); 

            if(p[i][j]!=0) 

           { 

               M.data[count].dat=p[i][j]; 

               M.data[count].i=i; 

               M.data[count].j=j; 

               M.tu++; 

               count++; 

           } 

 

        } 

   count=0; 

   FastConvert(M,&T); 

    for(i=0;i<T.tu;i++) 

    { 

       printf("%d ",T.data[count].dat); 

       count++; 

    } 

   free(p); 

    return0; 

//稀疏矩阵存储 

int FastConvert(TSMatrixM,TSMatrix *T

    int*num,*cpot,i,p,col; 

    //分别为一维指针分配内存 

    num=(int*)malloc(sizeof(int)*M.nu); 

    cpot=(int*)malloc(sizeof(int)*M.nu); 

 

    T->mu=M.nu;T->nu=M.mu;T->tu=M.tu; 

 

    if(T->tu) 

    {  

        //初始化每一列非零元素个数为零 

         for(i=0;i<M.nu;i++)  

            num[i]=0; 

         //初始化数组将其置为每一列的非零元素个数 

         for(i=0;i<M.tu;i++) 

            ++num[M.data[i].j]; 

         //cpot数组表示每一列第一个非零元素位置 

        cpot[0]=0; 

         for(i=1;i<M.nu;i++) 

             cpot[i]=cpot[i-1]+num[i-1]; 

         //进行矩阵值的交换 

         for(i=0;i<M.tu;i++) 

        { 

             col=M.data[i].j;  p=cpot[col]; 

              T->data[p].i=M.data[i].j; 

              T->data[p].j=M.data[i].i; 

              T->data[p].dat=M.data[i].dat; 

             ++cpot[col]; 

        } 

    } 

    return1;