系数矩阵的存储方式:三元组
typedef struct TRIPLE_ELEMENT {
int data;
int row;
int col;
}TRIPLE_ELEMENT;
三元组用于存储矩阵的每一个元素
额外需要一个表头来表示整个矩阵:
typedef struct TRIPLE_MATRIX {
TRIPLE_ELEMENT *elementList;
int maxRow;
int maxCol;
int elementCount;
}TRIPLE_MATRIX;
初始化及输入矩阵:
TRIPLE_MATRIX *initTripleMatrix(int maxRow, int maxCol, int elementCount) {
TRIPLE_MATRIX *result = NULL;
result = (TRIPLE_MATRIX *) calloc(sizeof(TRIPLE_MATRIX), 1);
result->elementList = (TRIPLE_ELEMENT *)
calloc(sizeof(TRIPLE_ELEMENT), elementCount);
result->maxRow = maxRow;
result->maxCol = maxCol;
result->elementCount = elementCount;
return result;
}
初始化矩阵需要动态申请两个空间:①表头(指向矩阵) ②元素个数为elementcount的数组(用于存储矩阵各元素)
void inputTripleMatrix(const TRIPLE_MATRIX *matrix) {
int index = 0;
int count = matrix->elementCount;
int row;
int col;
int data;
for (index = 0; index < count; index++) {
scanf("%d%d%d", &row, &col, &data);
matrix->elementList[index].row = row;
matrix->elementList[index].col = col;
matrix->elementList[index].data = data;
}
}
由用户输入矩阵内容
以上两个函数都比较好理解,没有太多需要深入思考的问题
打印矩阵:
void showTripleMatrix(const TRIPLE_MATRIX *matrix) {
int row;
int col;
int index = 0;
for (row = 0; row < matrix->maxRow; row++) {
for (col = 0; col < matrix->maxCol; col++) {
if (row == matrix->elementList[index].row
&& col == matrix->elementList[index].col) {
printf("%4d", matrix->elementList[index].data);
index++;
} else {
printf("%4d", 0);
}
}
printf("\n");
}
}
表面是遍历矩阵数据数组完成遍历,实际隐藏的内部逻辑有 需要矩阵元素数组先按行升序,同行需要按列升序
这为矩阵的转置添加了一个难题
转置矩阵:
我们的手工过程通常是直接将行列转换即可,但是由于打印矩阵函数的内部逻辑,我们交转行列还需要进行排序,
最笨的办法就是数组内的元素直接交换行列,再依据行列依次升序排列即可。
但是有更方便的办法:
indexArr = (int *) calloc(sizeof(int), matrix->maxCol + 1);
for (index = 0; index < matrix->elementCount; index++) {
indexArr[matrix->elementList[index].col + 1]++;
}
我们首先构建一个数组, 目的是统计出转置后当前行上一行的非零元素个数
例如这组数据,转置后的第七行前一行第六行按数组表示有两个元素。
for (index = 1; index < matrix->maxCol + 1; index++) {
indexArr[index] += indexArr[index - 1];
}
接着数组初始化完成后,再进行一次追加操作,这样数组含义就发生了改变:
当前数组下标为转置后的行,其对应的数组元素表示此行第一个元素在数组中对应的下标。
因为追加之前表示的是该行上一行有效元素个数,那么追加后就是该行之前全部有效元素个数,而矩阵数据数组就是用来存放有效数组,下标就是该数据之前的有效元素个数,所以这两个数值是对应的。
左侧是转置前的元素,右侧是转置后的,按照我们制造出来的数组,转置后第七行(转之前第七列)的第一个元素应该在第九个位置存储。
通过这个数组和矩阵,我们就可以得出转置前的元素及其位置,还有转置后每行第一个非零元素在矩阵数组中的存储位置。
result = initTripleMatrix(matrix->maxCol, matrix->maxRow, matrix->elementCount);
for (index = 0; index < matrix->elementCount; index++) {
colIndex = matrix->elementList[index].col;
result->elementList[indexArr[colIndex]].row = matrix->elementList[index].col;
result->elementList[indexArr[colIndex]].col = matrix->elementList[index].row;
result->elementList[indexArr[colIndex]].data = matrix->elementList[index].data;
indexArr[colIndex]++;
}
接下来申请一个新的表头指向转置后的矩阵,开始转置工作。
首先数组下标对应的是转置后的行(即转置前的列),首先取出即将要操作的行,indexArr[colIndex]
可取出 当前行第一个有效元素在转置后的元素数组中的下标(即我们找到了转置后元素所要存放的位置),找到位置下一步就进行赋值操作,行列互换,值不变。
在我们赋值过后,有一个细节,我们现在对该行第一个元素进行赋值,因为我们的矩阵先后按行按列升序排序,所以在矩阵元素数组中同一行下一个元素一定在同一行上一个元素紧挨着之后存放。
当前行第一个有效元素位置indexArr[colIndex]
, 那么下一个有效元素位置就是上一个有效元素的下一个位置,即indexArr[colIndex]++
即可
总结
重点思想是矩阵转置中对转置后元素所存下标进行获取,通过得到每一前行有效元素个数,再进行追加就能得到前所有行的有效元素个数,这样和转置后矩阵数据数组的下标意义相同。