写在前面
课程采用教材《数据结构(C语言版)》严蔚敏,吴伟民,清华大学出版社。
本系列博文用于自我学习总结和期末复习使用,同时也希望能够帮助到有需要的同学。如果有知识性的错误,麻烦评论指出。
本次实验实现稀疏矩阵的加法运算。
至此,数据结构课程的学习也告一段落,因为专业的原因,与科班的课程相比,做了不少简化,主要是面对测绘专业的学习。相关的课程资源,以及程序源文件都放在链接资源中了,需者自取。
相关资源
基本操作的实现方法
仍然采用头文件TSMatrixH.h,具体构造方法不再赘述。
目标:求取矩阵M和T的加和结果N。
具体过程:
基本的想法是,按照行主序,求M和T的元素在矩阵中的先后顺序,在前先插到N表尾,位置相同时,先求和,若和非零,插入到N表尾。最后将剩余的元素插入到N表尾。
具体编程实现时,需要用到二维数组的映像函数,以行主序计算M和T表当前位置的元素在矩阵中相对LOC(0,0)的位置。计算公式为:off=col×i+j。再定义三个表循环位置,初始位置均为0。然后开始遍历M和T表,此次遍历完全遍历了表尾元素在矩阵中位置靠前的表,另一表要么也遍历完全,要么还有剩余。在遍历中,判断两表当前位置元素在矩阵中的位置前后关系。如果,位置相同,则两表当前位置元素做加和运算,若和非零,则插入到N表尾,三表的位置后移一位,N表长加1;若为零,则对N表不操作,M和T表位置后移一位。如果,某一表位置在前,则先插入该表当前位置的元素,该表和N表位置后移一位,N表长加1。最后,将剩余的元素逐个插入到N表尾。
具体实现
//1、用到的头文件、命名空间和函数执行结果状态代码
#include <iostream>
#include "TSMatrixH.h"
using namespace std;
//2、基本操作侧函数原型说明及实现
//-----基本操作的函数原型说明-----
Status TSMatrixAdd(TSMatrix M, TSMatrix T, TSMatrix& N);
// 初始条件:采用三元组表存储表示,T,M为同维度稀疏矩阵。
// 操作结果:求M和T的加和矩阵,并用N传出。
Status TSMatrixAdd(TSMatrix M, TSMatrix T, TSMatrix& N) {
// 求M和T的加和矩阵,并用N传出。
if (!(M.col == T.col && M.row == T.row)) { cout << "矩阵维度不一致!!\n"; return ERROR; }
N.col = M.col; N.row = M.row; N.num = 0;// 获取矩阵行列数,非零元个数置零
int locM = 0, locT = 0, locN = 0;// 三个三元表的位置,初始为0,都从表头开始
while (locT < T.num && locM < M.num)// 遍历表尾元素在矩阵中位置靠前的表
{// off为loc位置相对矩阵中LOC(0,0)位置行主序的偏移量
int offM = M.data[locM].i * M.col + M.data[locM].j;
int offT = T.data[locT].i * T.col + T.data[locT].j;
if (offM == offT)// 如果当前两三元组中的元素在矩阵中位于同一位置
{// 则求两元素的和,存放在e中
int e = T.data[locT].e + M.data[locM].e;
if (e)// 如果e非零
{// 则将该元素插入N表中
N.data[locN] = T.data[locT];// 获取该元素的行列
N.data[locN].e = e;// 获取该元素的值
locN++; locT++; locM++; N.num++;// 三个loc均后移一位,N表长加1
}
else// 如果e为0
{// 则跳过该元素,对N表无操作
locT++; locM++;// M和T表位置后移一位
}
}
else if (offM < offT)// 如果当前两三元组中的元素在矩阵中M的位置较前
{// 则先将M表当前位置的三元组插入到N表中
N.data[locN] = M.data[locM];// 插入到N表尾
locN++; locM++; N.num++;// N和M表的位置后移一位,N表长加1
}
else if (offM > offT)// 如果当前两三元组中的元素在矩阵中T的位置较前
{// 则先将T表当前位置的三元组插入到N表中
N.data[locN] = T.data[locT];// 插入到N表尾
locN++; locT++; N.num++;// N和T表的位置后移一位,N表长加1
}
}// 至此,表尾元素在矩阵中位置靠前的表,还需要将剩余的元素插入到N表中
if (locT < T.num)// 如果T表位置未到表尾
{// 则T表中有剩余元素
int surplus = T.num - locT;// 剩余元素个数
for (N.num; N.num < locN + surplus; N.num++)// 从N的表尾逐个插入T表剩余的元素
N.data[N.num] = T.data[locT++];
}
else if (locM < M.num)
{
int surplus = M.num - locM;// 剩余元素个数
for (N.num; N.num < locN + surplus; N.num++)// 从N的表尾逐个插入M表剩余的元素
N.data[N.num] = M.data[locM++];
}
return OK;
}// TSMatrixAdd
int main()
{
ElemType* S1[10], * S2[10];
for (int i = 0; i < 10; i++)// 创建10*12的矩阵,由4个一维数组组成
{// 申请空间
S1[i] = new ElemType[12];
S2[i] = new ElemType[12];
}
for (int i = 0; i < 10; i++)
for (int j = 0; j < 12; j++)
{// 全部赋0
*(S1[i] + j) = 0;
*(S2[i] + j) = 0;
}
S1[0][0] = 1;// 在(0,0)位置赋1
S1[2][2] = 2;// 在(2,2)位置赋2
S1[2][4] = 3;// 在(2,4)位置赋3
S1[6][9] = 4;// 在(6,9)位置赋4
S1[7][7] = -6;// 在(7,7)位置赋-6
S1[9][11] = 8;// 在(9,11)位置赋8
S2[0][0] = -1;// 在(0,0)位置赋1
S2[2][0] = 2;// 在(2,0)位置赋2
S2[3][4] = 3;// 在(3,4)位置赋3
S2[2][4] = 4;// 在(2,4)位置赋4
S2[7][7] = 6;// 在(6,11)位置赋6
TSMatrix M, T, N;// 创建三元组顺序表
M.CreateTSMatrix(M, S1, 10, 12);// 用三元组表存储稀疏矩阵
M.DispTSMat(M);// 输出三元组表
M.DispSMat(M);// 输出稀疏矩阵
T.CreateTSMatrix(T, S2, 10, 12);// 用三元组表存储稀疏矩阵
T.DispTSMat(T);// 输出三元组表
T.DispSMat(T);// 输出稀疏矩阵
TSMatrixAdd(M, T, N);// N=M+T
N.DispTSMat(N);// 输出三元组表
N.DispSMat(N);// 输出稀疏矩阵
return 0;
}
运行结果
总结
需要说明的是,为了演示方便,并没有满足稀疏矩阵的要求,但也可近似看做稀疏矩阵。值的一提的一点是,采用了偏移量来确定两表插入的顺序,更加直观,便于理解。避免直接比较行列出现逻辑错误。
简单分析不难发现,该算法的时间复杂度为O(max{M.num,T.num})。第一个遍历,把表尾元素在矩阵中位置靠前的表先遍历完了,不论表是否较长,另一个未完全遍历的表在处理剩余元素时,将未遍历的元素也遍历了一遍,故,循环语句执行次数为两表表长的较大值。
此前采取的方法是,把一表加到另一表上,这必然会引起双重循环语句,增加时间复杂度。因为,将一表的元素插入另一表,或将一表的元素删除,必然会涉及多个元素的移动,需要用循环语句来实现,加上外层遍历用的循环,所以至少需要两层循环。如果这种方法有其他比较好的处理方式,也愿闻其详。
当然,也可以参照快速转置的思想,通过一定的方法,预先获取各元素的位置,或许也是一种可行的方法。对此,我并没有深入研究了,但可以肯定的是,至少会用到多个单层的循环语句。