三元组表和十字链表存储稀疏矩阵,并进行矩阵的加、乘法、转置等操作
三元组表:存储稀疏矩阵的非零元素,以及该元素所在的行、列信息,极大的节省了空间(如相比于一般的二维数组的存储),而且三元组表的某些算法的时间效率也要优于经典算法,如基于三元组表的一次快速转置算法等等
十字链表:当要进行矩阵的加、减、乘等运算时,有时非零元素的位置会发生很大的变化,三元组表位保持以行或列序为主而大量移动元素,十字链表就比较灵活,它在三元组表的基础之上(元素e、行row、列col),多了两个指针域down和right(名称随意),right指向同一行中的下一个非零元素,down指向同一列的下一个非零元素
a) 稀疏矩阵的三元组表顺序存储结构:
用结构体struct--->定义三元组表,在main()函数中先规划好对稀疏矩阵进行的操作createMatrix()、addMatrix()、multiplyMatrix()、transMatrix()、locMatrix()、output()等,其中转置运算分为class_TransMatrix()和fast_TransMatrix(),即经典转置与快速转置
当然用三元组顺序表存储稀疏矩阵并进行处理,第一步要先构建出一个“三元组表”的“数据类型”,根据这个“数据类型”定义相应的变量来存储稀疏矩阵中非零元素的信息,接下来对稀疏矩阵运用三元组表的特性对其进行创建矩阵createMatrix()、矩阵相加addMatrix()、矩阵相乘multiplyMatrix()、矩阵转置transMatrix()、查找locMatrix()、矩阵输出output()等运算。<注:功能分为不同的函数可增强代码的复用性以及简化逻辑上的理解>
//三元组表的定义
typedef struct
{
int row, col;//行、列
ElemType e;//存储的元素
}Tripe;
typedef struct
{
Tripe data[MAX_SIZE + 1];//非零元素的三元组表
int m, n, len;//矩阵的行、列和非零元素的个数
}TSMatrix;
下面具体分析各个函数的构成
在创建矩阵之前,可以建立一个初始化函数
void initMatrix(TSMatrix *M)
{
M->len = M->m = M->n = 0;
}
<1>在创建稀疏矩阵createMatrix(TSMatrix *M){}中
b) 先输入稀疏矩阵的行数、列数和非零元素的个数,要注意稀疏矩阵的定义,其中非零元素的个数不得大于总元素的30%,若输入不合要求或数据类型不合法,可通过while()循环要求重新输入正确范围内的值
c) 输入元素e以及相应的行m、列n的信息,若输入m、n超出范围或e为零或数据类型不合法,都可通过while()循环要求重新输入正确范围内的值
d) 最后存储相应的行、列、值的信息
<2>在计算两个稀疏矩阵Q = M + N的加法addMatrix(TSMatrix M, TSMatrix N, TSMatrix *Q){}中(对于参数而言,前面两个传值,第三个传地址,这样可以有效地避免了M、N中数据的改变带来的对原数据影响,同时也可以返回所求Q的数据)
e) 首先判断是否符合两个矩阵相加的条件(同型矩阵才能相加)
f) 给Q进行初始化Q->m = M.m; Q->n = M.n;Q->len = 0;//长度先初始化为零,因为非零M + N可能会出现元素为0的情况
g) 先通过一个for(; i <= M.len && j <= N.len; i++, j++)循环来不断地进行M.data[i].col 和N.data[j].col && M.data[i].row 和 N.data[j].row的比较,看矩阵M、N中非零元素的行与行、列与列的关系看元素是否能相加(相加,其实只要结合矩阵相加的公式就很容易理解了,即两矩阵M、N中相同位置的元素进行相加),每循环一次就进行一次Q->len++,统计Q中非零元素的个数
h) 接着看上一步中for()循环结束后,看M或N是否还有非零元素剩余,若有的话,就将M或N中非零元素加入到Q中,并存储其相应的行、列的信息,最后进行Q->len++的操作,统计Q中非零元素的个数
<3>在计算两个矩阵的乘法Q = M * N的multiplyMatrix(TSMatrix M, TSMatrix N, TSMatrix *Q){}中 (对于参数而言,前面两个传值,第三个传地址,这样可以有效地避免了M、N中数据的改变带来的对原数据影响,同时也可以返回所求Q的数据)
i) 首先判断是否满座矩阵乘法的定理,即矩阵M的列数要和矩阵N的行数相等(M.n == N.m)
j) 上述条件成立后,接下来我们需要借助两个int 指针变量()或int数组变量(num、pos)来存储记录矩阵N中每一行中非零元素的个数和每一行中第一个非零元素的位置(指针的话需要申请内存空间)
k) 然后用一个for()循环,对第一个矩阵M中的非零元素为主线进行矩阵的乘法,并存储相应的非零元素到Q中
for(; cur <= M.len;)//以第一个矩阵的非零元素为主线进行矩阵的乘法
{
row_m = M.data[cur].row;//矩阵M的当前行数
for(int i = 1; i <= N.n; i++) t[i] = 0; //将每一行的计算结果的初值赋值为 0
for(; cur <= M.len && M.data[cur].row == row_m;) //计算相应的 M 中的行与 N 中的列的 乘积
{
col_m = M.data[cur].col;//一行一行地扫描,M中当前元素的列号
for(int i = pos[col_m]; i < pos[col_m + 1]; i++)//从 N 中对应的列开始找,找到符合条件的便与 M 中相应的行相乘
{
col_n = N.data[i].col;//N中相乘元素的列号
if(col_n == row_m) t[col_n] += M.data[cur].e * N.data[i].e;//M的行与N中对应的列相乘
}
cur++;
}
for(int i = 1; i <= N.n; i++) //压缩存储非0元素
if(t[i] != 0)
{
lenn++;
Q->data[lenn].row=row_m;
Q->data[lenn].col=i;
Q->data[lenn].e=t[i];
}
}
l) 最后进行M.m;Q->n=N.n;Q->len=lenn;并return就可以了
<4>转置矩阵中的列序递增转置class_TransMatrix(TSMatrix M, TSMatrix *N){}(为什么要列序呢?因为转置后不就是以按行序为主了嘛,符合习惯罢了)
if(N->len)
{
int j = 1;//计数器,记录转置后的三元组在 B 中的下标值
for(int k = 1; k <= M.n; k++)//扫描三元组表 M.n 次,每次寻找列值为 k 的值进行转置,转置后就会变成 以行序为主了嘛
{
//从头到尾将 M 中的每个非零元素都扫描一遍, 寻找列序为 k 的元素,然后交换行列
//或许可以多加一个变量,每次循环多存储一个值,可以用空间来换时间,多用一个变量的空间换来时间上几乎减少一半的效果,优化代码的性能
for(int i = 1; i <= M.len; i++)
if(k == M.data[i].col)//找到列序为 k 的元素,交换行列
{
N->data[j].row = M.data[i].col;
N->data[j].col = M.data[i].row;
N->data[j].e = M.data[i].e;
j++;
}
}
}
<5>转置矩阵中的一次定位快速转置fast_TransMatrix(TSMatrix M, TSMatrix *N){}
m) 我们需要借助两个int 指针变量()或int数组变量(num、pos)来存储记录矩阵N中每一行中非零元素的个数和每一行中第一个非零元素的位置(指针的话需要申请内存空间)
n) 根据一次定位快速转置的方法具体实现方法,编写代码如下,有改动
if(N->len)
{
for(col = 1; col <= M.n; col++)//每一列中的元素个数初始化为 0
num[col] = 0;
for(int i = 1; i <= M.len; i++)//统计每一列的非零元素的个数
num[M.data[i].col]++;
//printf("\n%d\n", num[1]);
//pos[1] = 1;//第一列第一个元素的位置,。。。咦,若第一列没有非零元素会怎么样??糟糕透了。。。
//修改后代码如下
for(int i = 1; i <= M.n; i++)
{
if(num[i])
{
pos[i] = 1;
k = i;
//printf("\n%d\n", k);
break;
}
}
for(col = k + 1; col <= M.n; col++)//每一列第一个非零元素所在位置
pos[col] = pos[col-1] + num[col-1];
for(int i = 1; i <= M.len; i++)//从头到尾扫描一遍
{
col = M.data[i].col;//矩阵 M 中非零元素所在的列
j = pos[col];//j相当于转置后的下标索引 1~M.len,寻找矩阵 N 列中非零元素的位置,这句话是关键
N->data[j].row = M.data[i].col;
N->data[j].col = M.data[i].row;
N->data[j].e = M.data[i].e;
pos[col]++;//指向同一列中的下一个元素,以便下一次遇到该列时的 j = pos[col]
}
}
<6>定位元素e所在矩阵中的位置locMatrix(TSMatrix M, ElemType e, int *x, int *y){},这个定位只要从头到尾遍历一遍就好了,一个for()循环就可以解决,找到第一个元素为e的值所在的行与列(其实可以扩展一下,找到矩阵中所有元素为e所在的行与列,思路:用一个指针动态地申请内存,每找到一个元素e,就申请两个指针空间分别存储并返回的行与列)
<6>输出output(TSMatrix M){},一个for()循环便可输出矩阵的元素值以及行与列的信息
o) 稀疏矩阵的十字链表存储结构:
“十字链表”,形似“十”字,由许多“十”字型交叉组合在一起,通过链表之间的相连,构成了一个个的“十”字,此为十字链表。
用结构体struct--->定义十字链表,在main()函数中先规划好对稀疏矩阵进行的操作createMatrix()、addMatrix()、multiplyMatrix()、transMatrix()、locMatrix()、output()等
当然用十字链表存储稀疏矩阵并进行处理,第一步要先构建出一个“十字链表”的“数据类型”,根据这个“数据类型”定义相应的变量来存储稀疏矩阵中非零元素的信息,接下来对稀疏矩阵运用十字链表的特性对其进行创建矩阵createMatrix()、矩阵相加addMatrix()、矩阵相乘multiplyMatrix()、矩阵转置transMatrix()、查找locMatrix()、矩阵输出output()等运算。<注:功能分为不同的函数可增强代码的复用性以及简化逻辑上的理解>
//十字链表的定义
typedef struct OLNode
{
int row, col;
ElemType e;
struct OLNode *right, *down;//同一行、同一列中的下一个非零元素
}OLNode, *OLink;
typedef struct
{
OLink *row_head, *col_head;//指向指针的指针,当作数组来用
int m, n, len;//矩阵的行数、列数、非零元素的个数
}CrossList;
下面分析各个函数的具体构成:
在创建矩阵之前,可以建立一个初始化函数
void initMatrix(CrossList *M)
{
M->col_head = M->row_head = NULL;
M->len = M->m = M->n = 0;
}
<1>createMatrix(CrossList *M){}
p) 输入稀疏矩阵M的行数M->m、列数M->n以及非零元素的个数M->len,判断输入的数据类型是否合法以及是否满足稀疏矩阵的非零元素的个数30%的要求,不满足的话则重新输入
q) 矩阵M经过上述的初始化后,接下来先为十字链表的(行与列的)头指针各申请一组空间(行与列的指针是二维指针,可以当作数组来使用)
r) 将头指针的指向都赋值为NULL,毕竟不一定每行每列都有元素嘛
s) 接下来输入非零值e以及对应的行与列的信息,并将其元素信息存储在一个中间节点指针*p中,并将其插入到所在行与列(元素e中含有行与列的信息)的末尾处,即链表的不断插入操作
<2>在矩阵的加法Q = M + N中,即addMatrix(CrossList M, CrossList N, CrossList *Q){}中
t) 先判断是否为同型矩阵,及行与行,列于列相等才能相加
u) 条件符合后先初始化矩阵Q,Q->m = M.m;Q->n = M.n;Q->len = 0;
v) 然后为头指针申请一组内存空间,若申请成功,则继续,并将头指针的指向先初始化为NULL
w) 通过for(int i = 1; i <= M.m; i++)一行行的进行相加,并通过嵌套的for()循环进行矩阵M和N的行与行&&列与列的比较看是两者相加或是其中的一个元素的赋值,这个联系矩阵的加法定理就容易多了,每循环一次就进行一次Q->len++,统计Q中非零元素的个数
x) 接着看上一步中for()循环结束后,看M或N是否还有非零元素剩余,若有的话,就将M或N中非零元素加入到Q中,并存储其相应的行、列的信息,最后进行Q->len++的操作,统计Q中非零元素的个数
<3>矩阵的乘法Q = M * N, multiplyMatrix (CrossList M, CrossList N, CrossList *Q)中
a) 先判断if(M.n != N.m),符合条件,则继续
b) 初始化Q->m = M.m;Q->n = M.n;Q->len = 0;
c) 头指针的一组内存申请以及初始化为NULL
d) 十字链表乘法的关键,M中的每一行不断地与N中的每一列对应的元素相乘
row_M = M.row_head[i];//指向 M 第 i 行的首节点
col_N = N.col_head[j];//指向 N 第 j 列的首节点
e = 0;//每计算完一行就重新赋值为零
for(; row_M && col_N;)
{
printf("\n%d\n",row_M->e);
if(row_M->col > col_N->row) col_N = col_N->down;
else if(row_M->col < col_N->row) row_M = row_M->right;
else
{
e += row_M->e * col_N->e;
row_M = row_M->right;
col_N = col_N->down;
//printf("\n%d\n", e);
}
}
e) 在每一行与每一列的乘积结果中,将if(e)非零元素依次存储到Q中即可
<4>矩阵的转置transMatrix(CrossList M, CrossList *Q){}
a) 先初始化矩阵Q
b) 然后为Q的头指针变量申请一组内存空间,申请成功的话,将其头指针的只想都赋值为NULL
c) 十字链表转置的原理:根据行或列的那一组头指针,结合一个中间指针*p,先交换过行与列的顺序后,再将这个节点p插入Q中的行与列的头指针中。根据这个原理便可完成接下来的步骤了
<5>矩阵的定位locMatrix(CrossList M, ElemType e, int *r_m, int *c_m){} 这个定位只要从头到尾遍历一遍就好了,一个for()循环就可以解决,找到第一个元素为e的值所在的行与列(其实可以扩展一下,找到矩阵中所有元素为e所在的行与列,思路:用一个指针动态地申请内存,每找到一个元素e,就申请两个指针空间分别存储并返回的行与列)
<6>输出output(TSMatrix M){},一个for()循环便可输出矩阵的元素值以及行与列的信息
<7> 销毁矩阵所占用的资源destroyMatrix(CrossList M){}通过一个for()循环,并结合free()即可释放十字链表所占用的空间资源
//-----------------------------------------------------------------------------
核心在于自定义的两个“数据类型”,根据“数据类型”定义相关的变量进行一些列的操作
//三元组表的定义
typedef struct
{
int row, col;//行、列
ElemType e;//存储的元素
}Tripe;
typedef struct
{
Tripe data[MAX_SIZE + 1];//非零元素的三元组表
int m, n, len;//矩阵的行、列和非零元素的个数
}TSMatrix;
//十字链表的定义
typedef struct OLNode
{
int row, col;
ElemType e;
struct OLNode *right, *down;//同一行、同一列中的下一个非零元素
}OLNode, *OLink;
typedef struct
{
OLink *row_head, *col_head;//指向指针的指针,当作数组来用
int m, n, len;//矩阵的行数、列数、非零元素的个数
}CrossList;
流程图如下:
附:源代码如下:
三元组表:
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
//------------------
#defineMAX_SIZE 1000
typedefint ElemType;
//三元组表的定义
typedefstruct
{
int row, col;//行、列
ElemType e;//存储的元素
}Tripe;
typedefstruct
{
Tripe data[MAX_SIZE + 1];//非零元素的三元组表
int m, n, len;//矩阵的行、列和非零元素的个数
}TSMatrix;
//初始化
voidinitMatrix(TSMatrix *M)
{
M->len = M->m = M->n = 0;
}
//创建稀疏矩阵
voidcreateMatrix(TSMatrix *M)
{
int m, n, e;
printf("\n请输入稀疏矩阵的行数、列数和非零元素的个数:");
while(3 != scanf("%d%d%d",&M->m, &M->n, &M->len))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
//初始化
/*for(int i = 1; i <= m * n; i++)
{
M->data[i]->e = 0;
}*/
while(10 *M->len >= 3 * M->m *M->n)//两边同乘以 10 进行比较
{
printf("\n稀疏矩阵的非零元素需要低于总元素的百分之30才行,请重新创建\n");
printf("\n请输入稀疏矩阵的行数、列数和非零元素的个数:");
while(3 !=scanf("%d%d%d", &M->m, &M->n, &M->len))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
printf("\n按行序输入,m,n,e为非零元素的行、列和值:\n");
printf("m=? n=? e=?\n");
for(int i = 1; i <= M->len; i++)
{
while(3 !=scanf("%d%d%d", &m, &n, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
while(m < 1 || n < 1 || m> M->m || n > M->n)//超出范围
{
printf("行或列超出范围,请重新输入");
printf("m=? n=?e=?\n");
while(3 !=scanf("%d%d%d", &m, &n, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
while(!e)
{
printf("存储的e元素不能为零,请重新输入");
printf("m=? n=?e=?\n");
while(3 !=scanf("%d%d%d", &m, &n, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
//防止多个非零元素存放到了同一个位置
while(i >= 2 && m ==M->data[i-1].row && n == M->data[i-1].col)
{
printf("该位置已经存储过元素了,请重新输入");
printf("m=? n=?e=?\n");
while(3 !=scanf("%d%d%d", &m, &n, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
//printf("\n%d", e);
M->data[i].e = e;
M->data[i].row = m;
M->data[i].col = n;
//printf("\n%d", e);
}
//return true;
}
//矩阵之和
booladdMatrix(TSMatrix M, TSMatrix N, TSMatrix *Q)
{
if(M.m != N.m || M.n != N.n)
{
printf("相同类型矩阵才能相加");
return false;
}
Q->m = M.m;
Q->n = M.n;
Q->len = 0;//初始化
int i = 1, j = 1, lenn;
for(; i <= M.len && j <=N.len; i++, j++)
{
//Q->data[i]->e =M.data[i]->e + N.data[i]->e;
if(M.data[i].col == N.data[j].col&& M.data[i].row == N.data[j].row)
{
lenn = ++Q->len;
Q->data[lenn].e =M.data[i].e + N.data[j].e;
if(!Q->data[lenn].e)continue;//不能为零
Q->data[lenn].col =M.data[i].col;
Q->data[lenn].row =M.data[i].row;
}
else
{
if(M.data[i].col <N.data[j].col)
{
lenn = ++Q->len;
Q->data[lenn].e =M.data[i].e;
if(Q->data[lenn].e);
Q->data[lenn].col= M.data[i].col;
Q->data[lenn].row= M.data[i].row;
}
else if(M.data[i].col >N.data[j].col)
{
lenn = ++Q->len;
Q->data[lenn].e =N.data[j].e;
Q->data[lenn].col= N.data[j].col;
Q->data[lenn].row= N.data[j].row;
}
else
{
if(M.data[i].row<= N.data[j].row)
{
lenn = ++Q->len;
Q->data[lenn].e= M.data[i].e;
Q->data[lenn].col= M.data[i].col;
Q->data[lenn].row= M.data[i].row;
}
else
{
lenn = ++Q->len;
Q->data[lenn].e= N.data[j].e;
Q->data[lenn].col= N.data[j].col;
Q->data[lenn].row= N.data[j].row;
}
}
}
}
//如果M中还有剩余...
for(; i <= M.len; i++)
{
lenn = ++Q->len;
Q->data[lenn].e = M.data[i].e;
Q->data[lenn].col =M.data[i].col;
Q->data[lenn].row =M.data[i].row;
}
//如果N中还有剩余...
for(; j <= N.len; j++)
{
lenn = ++Q->len;
Q->data[lenn].e = N.data[j].e;
Q->data[lenn].col =N.data[j].col;
Q->data[lenn].row =N.data[j].row;
}
}
//矩阵的乘法
boolmultiplyMatrix(TSMatrix M, TSMatrix N, TSMatrix *Q)
{
if(M.n != N.m)
{
printf("不满足矩阵的乘法定理!");//第一个矩阵的列数(column)和第二个矩阵的行数(row)相同才行
return false;
}
int *num = (int *)malloc(sizeof(N.m +1));//N中非零元素的个数,下标从 1 开始
int *pos = (int *)malloc(sizeof(N.m +2));//N中各行中第一个非零元素的位置,多加一个作为结束的条件
ElemType *t = (int *)malloc(sizeof(N.m +1));//临时元素 t 的指针,当作数组来用,存储每一行的计算结果
if(0 != M.len * N.len)//确保Q不是非零矩阵
{
for(int i = 1; i <= N.m; i++)num[i] = 0;//每一行的个数先初始化为零
for(int i = 1; i <= N.len; i++)num[N.data[i].row]++;//各行非零元素的个数
pos[1] = 1;
for(int i =2; i <= N.m + 1;i++) pos[i] = pos[i-1] + num[i-1];//矩阵中第 i 行非零元素的开始位置
int cur = 1, lenn = 0;
int row_m, col_m, col_n;
for(; cur <= M.len;)//以第一个矩阵的非零元素为主线进行矩阵的乘法
{
row_m = M.data[cur].row;//矩阵M的当前行数
for(int i = 1; i <= N.n;i++) t[i] = 0;//将每一行的计算结果的初值赋值为 0
for(; cur <= M.len&& M.data[cur].row == row_m;)//计算相应的 M 中的行与 N 中的列的 乘积
{
col_m =M.data[cur].col;//一行一行地扫描,M中当前元素的列号
for(int i =pos[col_m]; i < pos[col_m + 1]; i++)//从 N 中对应的列开始找,找到符合条件的便与 M 中相应的行相乘
{
col_n =N.data[i].col;//N中相乘元素的列号
if(col_n ==row_m) t[col_n] += M.data[cur].e * N.data[i].e;//M的行与N中对应的列相乘
}
cur++;
}
for(int i = 1; i <= N.n;i++) //压缩存储非0元素
if(t[i] != 0)
{
lenn++;
Q->data[lenn].row=row_m;
Q->data[lenn].col=i;
Q->data[lenn].e=t[i];
}
}
Q->m=M.m;
Q->n=N.n;
Q->len=lenn;
}
return true;
//int i = 1, j = 1, k = 1, e, cur;
}
//列序递增转置法
voidclass_TransTSMatrix(TSMatrix M, TSMatrix *N)
{
N->m = M.n;
N->n = M.m;
N->len = M.len;
if(N->len)
{
int j = 1;//计数器,记录转置后的三元组在 B 中的下标值
for(int k = 1; k <= M.n; k++)//扫描三元组表 M.n 次,每次寻找列值为 k 的值进行转置,转置后就会变成 以行序为主了嘛
{
//从头到尾将 M 中的每个非零元素都扫描一遍, 寻找列序为 k 的元素,然后交换行列
//或许可以多加一个变量,每次循环多存储一个值,可以用空间来换时间,多用一个变量的空间换来时间上几乎减少一半的效果,优化代码的性能
for(int i = 1; i <=M.len; i++)
if(k ==M.data[i].col)//找到列序为 k 的元素,交换行列
{
N->data[j].row= M.data[i].col;
N->data[j].col= M.data[i].row;
N->data[j].e= M.data[i].e;
j++;
}
}
}
}
//一次定位快速转置算法
voidfast_TransTSMatrix(TSMatrix M, TSMatrix *N)
{
int *num = (int *)malloc(sizeof(M.m +1));//N中非零元素的个数,下标从 1 开始
int *pos = (int *)malloc(sizeof(M.n +1));//N中各行中第一个非零元素的位置
N->len = M.len;
N->n = M.m;
N->m = M.n;
int col, j, k;
if(N->len)
{
for(col = 1; col <= M.n;col++)//每一列中的元素个数初始化为 0
num[col] = 0;
for(int i = 1; i <= M.len;i++)//统计每一列的非零元素的个数
num[M.data[i].col]++;
//printf("\n%d\n",num[1]);
//pos[1] = 1;//第一列第一个元素的位置,。。。咦,若第一列没有非零元素会怎么样??糟糕透了。。。
//修改后代码如下
for(int i = 1; i <= M.n; i++)
{
if(num[i])
{
pos[i] = 1;
k = i;
//printf("\n%d\n",k);
break;
}
}
for(col = k + 1; col <= M.n;col++)//每一列第一个非零元素所在位置
pos[col] = pos[col-1] +num[col-1];
for(int i = 1; i <= M.len;i++)//从头到尾扫描一遍
{
col = M.data[i].col;//矩阵 M 中非零元素所在的列
j = pos[col];//j相当于转置后的下标索引 1~M.len,寻找矩阵 N 列中非零元素的位置,这句话是关键
N->data[j].row =M.data[i].col;
N->data[j].col =M.data[i].row;
N->data[j].e =M.data[i].e;
pos[col]++;//指向同一列中的下一个元素,以便下一次遇到该列时的 j = pos[col]
}
}
}
//定位值为e的元素
intlocMatrix(TSMatrix M, ElemType e, int *x, int *y)
{
for(int i = 1; i <= M.len; i++)
{
if(e == M.data[i].e)
{
*x = M.data[i].row;
*y = M.data[i].col;
return 1;
}
}
return 0;
}
//输出
voidoutput(TSMatrix M)
{
printf("\n行 列 元素值\n\n");
for(int i = 1; i <= M.len; i++)
{
printf("%2d%4d%8d\n",M.data[i].row, M.data[i].col, M.data[i].e);
}
printf("\n");
}
intmain()
{
system("color f5");
//int i;
//printf("%d", i);Dev-C++编译器竟然输出的是0!!
TSMatrix M_One, M_Two, M_Three;
initMatrix(&M_One);
initMatrix(&M_Two);
initMatrix(&M_Three);
ElemType e;
int k;
int m_x, m_y;
for(; k != 6;)
{
printf("\n");
printf("\n 1. 求两个矩阵之和");
printf("\n 2. 求两个矩阵之积");
printf("\n 3. 矩阵转置的经典算法, 即按列序递增转置法");
printf("\n 4. 一次定位快速转置法");
printf("\n 5. 查找值为e的元素");
printf("\n 6. 结束程序运行");
printf("\n======================================");
printf("\n 请输入您的选择(1, 2, 3, 4, 5, 6)");
while(1 !=scanf("%d",&k))
{
printf("输入不合法,请重新输入");
fflush(stdin);
}
switch(k)
{
case 1:
createMatrix(&M_One);
createMatrix(&M_Two);
addMatrix(M_One,M_Two, &M_Three);
printf("\n矩阵相加的结果:\n");
output(M_Three);
break;
case 2:
createMatrix(&M_One);
createMatrix(&M_Two);
multiplyMatrix(M_One,M_Two, &M_Three);
printf("\n矩阵相乘的结果:\n");
output(M_Three);
break;
case 3:
createMatrix(&M_One);
//output(M_One);
class_TransTSMatrix(M_One,&M_Two);
printf("\n转置的结果:\n");
output(M_Two);
break;
case 4:
createMatrix(&M_One);
//output(M_One);
fast_TransTSMatrix(M_One,&M_Two);
printf("\n转置的结果:\n");
output(M_Two);
break;
case 5:
createMatrix(&M_One);
//output(M_One);
printf("\ne=?");
scanf("%d",&e);
if(locMatrix(M_One,e, &m_x, &m_y))
printf("\n元素e在矩阵中的第 %d 行,第 %d 列\n", m_x, m_y);
else
printf("\n查无此元素!\n");
break;
case 6:
break;
default :
printf("\n输入的数字不在范围内,即将清屏...\n");
}
Sleep(1000);
printf("\n3秒后清屏...");
Sleep(3000);
system("cls");
}
system("pause");
printf("\n退出\n");
return 0;
}
十字链表存储如下:
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
//--------------------
typedef int ElemType;
//十字链表的定义
typedef struct OLNode
{
int row, col;
ElemType e;
struct OLNode *right, *down;//同一行、同一列中的下一个非零元素
}OLNode, *OLink;
typedef struct
{
OLink *row_head, *col_head;//指向指针的指针,当作数组来用
int m, n, len;//矩阵的行数、列数、非零元素的个数
}CrossList;
//初始化
void initMatrix(CrossList *M)
{
M->col_head = M->row_head = NULL;
M->len = M->m = M->n = 0;
}
//构造十字链表矩阵
void createCrossList(CrossList *M)
{
int m, n, len;
printf("\n输入矩阵的行数、列数、非零元素的个数:");
while(3 != scanf("%d%d%d", &m, &n, &len))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
while(10 *len >= 3 * m * n)//两边同乘以 10 进行比较
{
printf("\n稀疏矩阵的非零元素需要低于总元素的百分之30才行,请重新创建\n");
printf("\n请输入稀疏矩阵的行数、列数和非零元素的个数:");
while(3 != scanf("%d%d%d", &m, &n, &len))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
M->m = m;//初始化
M->n = n;
M->len = len;
//判断头指针的空间是否申请成功
if(!(M->row_head = (OLink *)malloc((m + 1) * sizeof(OLink)))) exit(1);
if(!(M->col_head = (OLink *)malloc((n + 1) * sizeof(OLink)))) exit(1);
for(int i = 1; i <= m; i++)
M->row_head[i] = NULL;
for(int i = 1; i <= n; i++)
M->col_head[i] = NULL;
int i, j, e;
OLNode *p, *t;
printf("i=? j=? e=? \n");
int k = 1;//计数器
for(; k <= len; k++)
{
while(3 != scanf("%d%d%d", &i, &j, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
//防止多个非零元素存放到了同一个位置
while(k >= 2 && i == p->row && j == p->col)
{
printf("该位置已经存储过元素了,请重新输入");
printf("i=? i=? e=?\n");
while(3 != scanf("%d%d%d", &i, &j, &e))
{
printf("输入的数据类型不合法,请重新输入");
fflush(stdin);
}
}
if(i < 1 || j < 1) exit(-1);
if(!(p = (OLNode *)malloc(sizeof(OLNode)))) exit(1);
p->row = i;
p->col = j;
p->e = e;
p->right = p->down = NULL;
if(NULL == M->row_head[i]) M->row_head[i] = p;//当这行没有节点的时候,只有头指针,即p插向第一个节点处
else
{
for(t = M->row_head[i]; NULL != t->right && t->right->col < j; t = t->right) ;//比较,看插入已有节点的前面还是后面,移动到该插入位置的前一个元素
//下面两行 插入元素到对应的位置,完成行的插入
p->right = t->right;
t->right = p;
}
//同理可得,完成列的插入
if(NULL == M->col_head[j]) M->col_head[j] = p;
else
{
for(t = M->col_head[j]; NULL != t->down && t->down->row < i; t = t->down) ;//比较,看插入已有节点的前面还是后面,移动到该插入位置的前一个元素
//下面两行 插入元素到对应的位置,完成列的插入
p->down = t->down;
t->down = p;
}
}
}
//两个矩阵之和
bool addMatrix(CrossList M, CrossList N, CrossList *Q)
{
if(M.m == N.m && M.n == N.n)//同型矩阵才能相加
{
//初始化矩阵
Q->m = M.m;
Q->n = M.n;
Q->len = 0;
//为Q的头指针申请内存
if(!(Q->row_head = (OLink *)malloc((Q->m + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
if(!(Q->col_head = (OLink *)malloc((Q->n + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
//初始化 Q 的头指针向量,置为空链表
for(int i = 1; i <= Q->m; i++) Q->row_head[i] = NULL;
for(int i = 1; i <= Q->n; i++) Q->col_head[i] = NULL;
OLink p_m, p_n, p;//指针类型变量,由结构体定义可得
OLink t_row, t_col;
for(int i = 1; i <= M.m; i++)
{
p_m = M.row_head[i];//指向 M 中第i行的第一个节点
p_n = N.row_head[i];//指向 N 中第i行的第一个节点
//if(p_m) printf("hahahahahahahaha");
for(; p_m && p_n;)
{
if(!(p = (OLink)malloc(sizeof(OLNode))))//申请一个节点
{
printf("节点申请失败!");
return false;
}
//printf("\n%d %d\n", p_m->e, p_n->e);
if(p_m->col < p_n->col)
{
p->col = p_m->col;
p->row = p_m->row;
p->e = p_m->e;
p->right = NULL;
p->down = NULL;
p_m = p_m->right;//指针右移
Q->len++;
}
else if(p_m->col > p_n->col)
{
p->col = p_n->col;
p->row = p_n->row;
p->e = p_n->e;
p->right = NULL;
p->down = NULL;
p_n = p_n->right;//指针右移
Q->len++;
}
else if(p_n->e + p_m->e)
{
p->col = p_n->col;
p->row = p_n->row;
p->e = p_n->e + p_m->e;
//printf("\n%d\n", p->e);
p->right = NULL;
p->down = NULL;
p_n = p_n->right;//指针右移
p_m = p_m->right;
Q->len++;
}
else
{
p_m = p_m->right;
p_n = p_n->right;
continue;//和为零,指针向后移动,循环重新计算
}
//p插入Q中
if(NULL == Q->row_head[i])
Q->row_head[i] = t_row = p;//第一个节点
else
{
t_row->right = p;//行插入
t_row = p;//向后移动
}
if(NULL == Q->col_head[p->col])
Q->col_head[p->col] = t_col = p;
else
{
t_col->down = p;//列插入
t_col = p;
}
//free(p);不能释放啊,差点想错了
}
//M中剩余元素添加到Q中
for(; p_m;)
{
if(!(p = (OLink)malloc(sizeof(OLNode))))
{
printf("内存申请失败!");
return false;
}
p->col = p_m->col;
p->row = p_m->row;
p->e = p_m->e;
p->right = NULL;
p->down = NULL;
p_m = p_m->right;//指针右移
Q->len++;
//p插入Q中
if(NULL == Q->row_head[i])
Q->row_head[i] = t_row = p;//第一个节点
else
{
t_row->right = p;//行插入
t_row = p;//向后移动
}
if(NULL == Q->col_head[p->col])
Q->col_head[p->col] = t_col = p;
else
{
t_col->down = p;//列插入
t_col = p;
}
}
//N中剩余元素添加到Q中
for(; p_n;)
{
if(!(p = (OLink)malloc(sizeof(OLNode))))
{
printf("内存申请失败!");
return false;
}
p->col = p_n->col;
p->row = p_n->row;
p->e = p_n->e;
p->right = NULL;
p->down = NULL;
p_n = p_n->right;//指针右移
Q->len++;
//p插入Q中
if(NULL == Q->row_head[i])
Q->row_head[i] = t_row = p;//第一个节点
else
{
t_row->right = p;//行插入
t_row = p;//向后移动
}
if(NULL == Q->col_head[p->col])
Q->col_head[p->col] = t_col = p;
else
{
t_col->down = p;//列插入
t_col = p;
}
}
}
//----------------------------
return true;
}
else
{
printf("非同型矩阵不能相加!");
return false;
}
}
//矩阵相乘
bool multiplyMatrix(CrossList M, CrossList N, CrossList *Q)
{
if(M.n != N.m)
{
printf("不符合矩阵相乘的定义!");
return true;
}
initMatrix(Q);
Q->m = M.m;
Q->n = N.n;
Q->len = 0;
//Q的头节点的初始化
if(!(Q->row_head = (OLink *)malloc((Q->m + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
if(!(Q->col_head = (OLink *)malloc((Q->n + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
for(int i = 1; i <= Q->m; i++)
Q->row_head[i] = NULL;
for(int i = 1; i <= Q->n; i++)
Q->col_head[i] = NULL;
//开始求积
OLink row_M, col_N, p, row_t, col_t;
int e;
for(int i = 1; i <= Q->m; i++)
for(int j = 1; j <= Q->n; j++)
{
row_M = M.row_head[i];//指向 M 第 i 行的首节点
col_N = N.col_head[j];//指向 N 第 j 列的首节点
e = 0;//每计算完一行就重新赋值为零
for(; row_M && col_N;)
{
//printf("\n%d\n",row_M->e);
if(row_M->col > col_N->row) col_N = col_N->down;
else if(row_M->col < col_N->row) row_M = row_M->right;
else
{
e += row_M->e * col_N->e;
row_M = row_M->right;
col_N = col_N->down;
//printf("\n%d\n", e);
}
}
//printf("\n%d\n", e);
if(e)
{
p = (OLNode *)malloc(sizeof(OLNode));
if(!p)
{
printf("内存申请失败!");
return false;
}
p->row = i;
p->col = j;
p->e = e;
p->right = NULL;
p->down = NULL;
if(NULL == Q->row_head[i])//行插入
Q->row_head[i] = row_t = p;
else row_t = row_t->right = p;
if(NULL == Q->col_head[j])//列插入
Q->col_head[j] = col_t = p;
else
{
//col_t = col_t->down = p;
for(; col_t->down; col_t = col_t->down) ;
col_t->down = p;
}
Q->len++;
}
}
return true;
}
//矩阵的转置
bool transMatrix(CrossList M, CrossList *Q)
{
Q->m = M.n;
Q->n = M.m;
Q->len = M.len;
//Q的头节点的初始化
if(!(Q->row_head = (OLink *)malloc((Q->m + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
if(!(Q->col_head = (OLink *)malloc((Q->n + 1) * sizeof(OLink))))
{
printf("内存空间申请失败!");
return false;
}
for(int i = 1; i <= Q->m; i++)
Q->row_head[i] = NULL;
for(int i = 1; i <= Q->n; i++)
Q->col_head[i] = NULL;
if(!M.len)
{
printf("没有什么可转置的嘛!");
return false;
}
OLink p, t, t_row, t_col;
//转置后以行序为主
for(int i = 1; i <= M.n; i++)
{
if(M.col_head[i])//第i列不为空
{
for(t = M.col_head[i]; t;)//遍历列中非零元素
{
if(!(p =(OLink)malloc(sizeof(OLNode))))
{
printf("内存空间申请失败!");
return false;
}
p->row = M.col_head[i]->col;
p->col = M.col_head[i]->row;
p->e = M.col_head[i]->e;
p->right = p->down = NULL;
//p插入Q中
if(NULL == Q->row_head[i])
Q->row_head[i] = t_row = p;//第一个节点
else
{
//for(; NULL != t_row->right && t_row->right->col < i; t_row = t_row->right) ;
//p->right = t_row->right;//行插入
//上述方法随行,可浪费了不少时间,因为遍历列一个个地从上往下,所以可以优化为如下代码
t_row->right = p;
t_row = p;//向后移动
}
if(NULL == Q->col_head[p->col])
Q->col_head[p->col] = t_col = p;
else
{
//for(; NULL != t_col->down && t_col->down->row < t->row; t_col = t_col->down) ;
//p->down = t_col->down;//列插入
//上述方法随行,可浪费了不少时间,遍历列一个个地从上往下,所以可以优化为如下代码
t_col->down = p;
t_col = p;
}
t = t->down;
}
}
}
return true;
}
//定位矩阵中元素 x 的位置
bool locMatrix(CrossList M, ElemType e, int *r_m, int *c_m)
{
OLNode *p;
for(int i = 1; i <= M.m; i++)
{
for(p = M.row_head[i]; p; p = p->right)
{
if(p->e == e)
{
*r_m = p->row;
*c_m = p->col;
return true;
}
}
}
return false;
}
void destroyMatrix(CrossList M)
{
int i;
OLNode *p, *q;
for(i=1; i<=M.m; i++) //按行释放结点
{
p=M.row_head[i];
while(p)
{
q=p;
p=p->right;
free(q);
}
}
free(M.row_head);
free(M.col_head);
M.row_head=M.col_head=NULL;
M.m=M.n=M.len=0;
}
//输出
void output(CrossList M)
{
OLNode *p;
printf("\n行 列 元素值\n\n");
for(int i = 1; i <= M.m; i++)
{
p = M.row_head[i];
for(; p;)
{
printf("%2d%4d%8d\n", p->row, p->col, p->e);
p = p->right;
}
}
printf("\n");
}
int main()
{
system("color f5");
CrossList M, N, Q;
initMatrix(&M);
initMatrix(&N);
initMatrix(&Q);
ElemType x;
int r_m, c_m;
for(int k; k != 5;)
{
printf("\n");
printf("\n 1. 求两个矩阵之和");
printf("\n 2. 求两个矩阵之积");
printf("\n 3. 矩阵的转置");
printf("\n 4. 查找值为x的结点");
printf("\n 5. 结束程序运行");
printf("\n======================================");
printf("\n 请输入您的选择(1, 2, 3, 4, 5)");
while(1 != scanf("%d",&k))
{
printf("输入不合法,请重新输入");
fflush(stdin);
}
switch(k)
{
case 1:
createCrossList(&M);
createCrossList(&N);
addMatrix(M, N, &Q);
printf("\n矩阵相加的结果为:\n");
output(Q);
break;
case 2:
createCrossList(&M);
createCrossList(&N);
multiplyMatrix(M, N, &Q);
printf("\n矩阵相乘的结果为:\n");
output(Q);
break;
case 3:
createCrossList(&M);
printf("\n原矩阵为:\n");
output(M);
transMatrix(M, &Q);
printf("\n矩阵转置的结果为:\n");
output(Q);
break;
case 4:
createCrossList(&M);
printf("\n矩阵为:\n");
output(M);
printf("\nx = ? ");
scanf("%d", &x);
if(locMatrix(M, x, &r_m, &c_m))
printf("\n矩阵中的第一个该元素 %d 在矩阵的第 %d 行, 第 %d 列:\n", x, r_m, c_m);
else printf("\n矩阵中不存在此元素!\n");
break;
case 6:
break;
default :
printf("\n输入的数字不在范围内,即将清屏...\n");
}
Sleep(1000);
printf("\n3秒后清屏...");
Sleep(3000);
system("cls");
}
system("pause");
return 0;
}