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;
}