#define _CRT_SECURE_NO_WARNINGS
#define EMPTY_QUEUE_ERROR -999999
#include
#include
#include
#include
#include
#define MAXLEN 100
typedef int ElemType;
#define MAXNUM 40 // 假定非零元素个数不超过20个
/*
矩阵素材:
mat1:
0 0 0 0 0 1 0
0 0 0 2 0 0 0
0 3 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 2 0 0 0
mat2:
0 0 0 0 0 1 0
0 0 0 2 0 1 0
0 3 0 6 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
*/
/*(25)根据 二维数组 初始化一个稀疏矩阵结构体
二维数组中包含 少量非零元素,通过 遍历该二维数组,
将非零元素信息填入稀疏矩阵 结构体中。为简化起见,假定二维数组为 六行七列。
*/
#define ROWS 6
#define COLS 7
/*输入二维矩阵(含有一些非零元素*/
void BuildSparseMatrix(int m[ROWS][COLS], SparseMatrix* pSM)
{
int i, j, n = 0; // 用于统计非零元素个数
// 遍历该二维数组
for (i = 0;i<ROWS;i++)
for (j = 0;j<COLS;j++)
{
// 如果找到非零元素
if (m[i][j])
{
// 保存非零元素信息到(添加该元素到)三元组数组中相应的那个三元组中(三元组的三个分量(行,列,值)
pSM->elem[n].r = i+1;
pSM->elem[n].c = j+1;
pSM->elem[n].e = m[i][j];
++n;
}
}
// 分别保存稀疏矩阵的总行数、总列数和非零元素个数
pSM->nrow = ROWS;
pSM->ncol = COLS;
pSM->num = n;
}
/*矩阵中的单个元素(非零元素)的数据结构*/
typedef struct {
int r, c, e; // 非零元素的行、列和数据值/*从行和列1开始计数*/
}Triple;
/*稀疏矩阵整体(zheng'ti)的数据结构(已压缩)*/
typedef struct {
//非零元素信息数组
Triple elem[MAXNUM];/*elem[0]三元组保存的事整个矩阵的行数,列数,非零元素个数*/
//分别保存稀疏矩阵的总行数、总列数和非零元素个数
int nrow, ncol, num;
}SparseMatrix;/*sparse /spɑːrs/ adj.稀少的, 稀疏的*/
/*(26)输出稀疏矩阵(
(根据(gen'ju)稀疏矩阵压缩结构体,解压出原矩阵中(r,c)位置的元素) 该函数还可以配合循环,将三元组形式的稀疏矩阵按照二维数组的形式输出。稀疏矩阵的解压较为简单*/
int Get(SparseMatrix* pSM, int r, int c)
{
/**********************************************************
从稀疏矩阵中得到第r行c列元素的值
*********************************************************/
int i = 0;
/*遍历查找的过程:*/
// 遍历该稀疏矩阵的三元组数组
for (; i < pSM->num; i++)
{
// 若找到第r行第c列的非零值,则返回该值
if (pSM->elem[i].r == r && pSM->elem[i].c == c)
return pSM->elem[i].e;
}
// 若没有找到第r行第c列的数据,则必然是0
return 0;
}
void PrintSMatrix(SparseMatrix* pSM)
{
int i, j;
/*全遍历*/
for (i = 1; i <= pSM->nrow; i++)/*注意从1开始*/
{
for (j = 1; j <= pSM->ncol; j++)
printf("%d\t",Get(pSM,i,j)); // 输出第i行第j列元素的值(在主函数之外调用 &参数是愚蠢的.(定以复合函数时,内部函数的指针参数不要&比如:错误的Get(&pSM,i,j))
printf("\n");
}
}
/*(27)稀疏矩阵的转置
(快速转置):
引入了两个辅助的存储空间starting_pos和row_terms将A转置为B。
其中 数组row_terms中存放的是 各行 非零元素的 个数,
数组starting_pos用来存储 转置后的矩阵 每行非零元素的 起始地址。*/
void Translate3(SparseMatrix* pMatA, SparseMatrix* pMatB)
{
int i;
// 定义两个辅助数组;从0开始计数.
int row_terms[COLS], /*数组row_terms中存放的是 各行 非零元素的 个数*/
starting_pos[COLS];/*用来存储 转置后的矩阵 每行非零元素的 起始地址(在三元组数组中)*/
// 转置后,行列值交换,但非零元素个数不变
pMatB->ncol = pMatA->nrow;
pMatB->nrow = pMatA->ncol;
pMatB->num = pMatA->num;
// 统计转置后每行元素个数
// 首先将每行元素 个数置零
for (i = 0; i < pMatB->nrow; i++)
row_terms[i] = 0;
//遍历 一遍矩阵A中的 非零值信息,计算转置之后每行非零元素个数
/*核心步骤1(用数组row_terms[]巧妙地统计矩阵A中各列非零元素的个数(同时也是矩阵B各行的非0元素的个数.)
这种根据元素的信息填充/增加计数到对应列(对矩阵B来说是行)的方式比根据位置判断其上的元素(是否为0)一般都要要来的高效.*/
for (i = 0; i < pMatA->num; i++)/*从三元组列表中无空洞查找*/
row_terms[pMatA->elem[i].c - 1]++;
/*其中: pMatA->elem[i].c (是矩阵A的非零元素信息数组中第i个元素的所在列号,随着i的增长,这个表达式(记为k)的值的取值将落在 1,2,...pMatA->ncol 以内(且可以重复);注意在数组下标就要从0计数 (用户计数-1)*/
/*核心步骤2*/
/* 计算在转置之后 每一行(各行在非零元素(三元组)数组中)起始写入位置
显然, 第1行的写入位置也在三元组数组的0号位置 */
starting_pos[0] = 0;
/* 从第2行开始(下标i= 1), 递推公式:第i行的起始写入位置是其前一行 (第i-1行的起始写入位置+第i-1 行的非零元素个数)
迭代过程:(初始化/准备起点+迭代公式)*/
for (i = 1; i < pMatA->ncol; i++)
starting_pos[i] = starting_pos[i - 1] + row_terms[i - 1];
/*核心步骤3*/
// 有了两个辅助数组,再次扫描A矩阵(中的三元组数组表)
for (i = 0; i < pMatA->num; i++)
{
// A中的第i个非零值转置之后为B的第几行?pMatA->elem[i].c; 从starting_pos[]数组 中查找到第k行的在三元组数组中的起始写入位置:starting_pos[k](在三元组数组中的该位置处写入矩阵中的k行的下一个非0元素.
pMatB->elem[starting_pos[pMatA->elem[i].c - 1]].r = pMatA->elem[i].c;/*pMatA->elem[i].c,(不妨将该表达式记为k)即A中的第i个非零元素的所在列号,也是该元素(pMatA->elem[i])要填充在矩阵B的所在行的行号;再找到矩阵B中第k行的 起始位置:starting_pos[k],每填充一个,位置后移一,也就是说,起始位置starting_pos[k]是要不断迭代的;注意k没有变,变的是整体(starting_pos[k])这一变量:
该语句的化简写法:pMatB->elem[ starting_pos[k] - 1].r = k;
*/
pMatB->elem[starting_pos[pMatA->elem[i].c - 1]].c = pMatA->elem[i].r;
pMatB->elem[starting_pos[pMatA->elem[i].c - 1]].e = pMatA->elem[i].e;
// B中的第几行写入数据后, starting_pos中相应行的写入位置 后移一位
starting_pos[pMatA->elem[i].c - 1] ++;/*rting_pos[pMatA->elem[i].c - 1] 是一个整数,用以指示数组下标(引导填充位置)*/
}
}
/*(28)稀疏矩阵相加
试根据函数原型,写出稀疏矩阵C = A + B的函数。参数要求:两个参数矩阵的规格一摸一样,结果矩阵也是该规格:*/
void Add(SparseMatrix* pMatA, SparseMatrix* pMatB, SparseMatrix* pMatC)/*解压矩阵后再相加*/
{
int index = 0;
int x1 = 0,
x2 = 0;
for (int i = 1; i <= pMatA->nrow; i++)
{
for (int j = 1; j <= pMatB->ncol; j++)/*pMatA也行,任意,因为规格一样*/
{
x1 = Get(pMatA, i, j);
x2 = Get(pMatB, i, j);
if (x1 || x2)//这里的Get(又多写了&);注意短路问题呀:(x1 = Get(pMatA, i, j)) || (x2 = Get(pMatB, i, j))一旦前部非0,后部x2就无法获得更新/计算
{
/*三元组必须同时更改*/
pMatC->elem[index].e = x1 + x2;/*重新压缩的策略*/
pMatC->elem[index].r = i;/*i,j,r,c,均是从1开始计数*/
pMatC->elem[index].c = j;
index++;/*计数器,写入一个自增1*/
}
/* if(pMatA->)
pMatC->elem[index++] += pMatB->elem[j];*/
}//for
}//for
/*勿忘修改矩阵总体信息(规格,方便打印)建议放在函数头部:*/
pMatC->ncol = pMatA->ncol;
pMatC->nrow = pMatA->nrow;
pMatC->num = index ;
//index = 0;
}
/*(29)稀疏矩阵相乘
注意先明确操作函数的参数对象要求:矩阵规格必须分别是:a*k以及k*b,结果矩阵规格为a*b(计算每个C中的元素,都是一个k项式之和)
矩阵先后顺序不可掉换.
试根据函数原型,写出稀疏矩阵C = A * B的函数。*/
void Mul(SparseMatrix* pMatA, SparseMatrix* pMatB, SparseMatrix* pMatC)
{
/*处理矩阵C的统计信息*/
pMatC->nrow = pMatA->nrow;
pMatC->ncol = pMatB->ncol;
int x1,
x2;
int index = 0;
int temp_s = 0;
/*根据位置填值*/
for (int i = 0; i < pMatA->nrow; i++)/*矩阵A,B规格不相同了*/
{
for (int j = 0; j < pMatB->ncol; j++)
{
/*填充单个元素(求矩阵C的(i,j)位置上的元素值,并将非零元素的行列信息一并保存到三元组表中*/
temp_s = 0;
/*计算k项式的值(求和);同时也是结果矩阵的(i,j)位置上的元素.*/
for (int k = 1; k <= pMatA->ncol; k++)
{
temp_s += (Get(pMatA, i, k) * Get(pMatB, k, j));
if (temp_s)/*将其中的非0元素压到三元组数组中去*/
{
pMatC->elem[index].e = temp_s;
pMatC->elem[index].r = i;
pMatC->elem[index].c = j;
index++;
}
}
}
}
pMatC->num = index ;
}
/*(30)调试
根据上述稀疏矩阵定义,,完成下面代码,运行并调试程序。*/
void fill_two_dimenssion_array_2(int(*arr_2)[COLS])
{
printf("创建并填充二维数组:\n");
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
scanf("%d", &arr_2[i][j]);
}
}
}
int main()
{
// 定义两个二维数组a和b,用少量 非零值 初始化a和b
int a[ROWS][COLS],
b[ROWS][COLS];
fill_two_dimenssion_array_2(a);
fill_two_dimenssion_array_2(b);
//定义两个三元组形式稀疏矩阵mat1和mat2,分别用a和b初始化
SparseMatrix
mat1,
mat2,
mat3,
mat4;
/*一般的,矩阵mat1,mat2本身不受二维数组a,b规格的影响
但矩阵mat_i中元素对以矩阵mat_i为参数的函数来说有影响*/
BuildSparseMatrix(a, &mat1);
BuildSparseMatrix(b, &mat2);
//输出mat1和mat2
/**/
printf("输出mat1:\n");
PrintSMatrix(&mat1);
printf("输出mat2:\n");
PrintSMatrix(&mat2);
/**/
// 定义三元组形式稀疏矩阵mat3, 计算mat3=mat1+mat2
//输出mat3
printf("测试Add(&mat1, &mat2, &mat3),并输出mat3:\n");
Add(&mat1, &mat2, &mat3);
/* */
PrintSMatrix(&mat3);
// 将mat3转置到mat2
printf("测试Translate3(&mat3, &mat2):将mat3转置到mat2:\n");
Translate3(&mat3, &mat2);
//输出mat2
/* */printf("输出矩阵转置结果(新)mat2:\n");
PrintSMatrix(&mat2);
// 定义三元组形式稀疏矩阵mat4, 计算mat4=mat1 * mat2
printf("测试Mul(&mat1, &mat2, &mat4);并输出矩阵mat4:\n");
Mul(&mat1, &mat2, &mat4);
//输出mat4
PrintSMatrix(&mat4);
}
/*mat1:
0 0 0 0 0 1 0
0 0 0 2 0 0 0
0 3 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 2 0 0 0
mat2:
0 0 0 0 0 1 0
0 0 0 2 0 1 0
0 3 0 6 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
*/