当前位置: 首页 > news >正文

数据结构实验6.2:稀疏矩阵的基本运算

文章目录

  • 一,实验目的
  • 二,问题描述
  • 三,基本要求
  • 四、算法分析
    • (一)稀疏矩阵三元组表示法存储结构
    • (二)插入算法
    • (三)创建算法
    • (四)输出算法
    • (五)相加算法
    • (六)相乘算法
    • (七)转置算法
  • 五,实验操作
  • 六,示例代码
  • 七,运行效果


一,实验目的

  1. 深入理解数组的存储结构与操作原理,熟练掌握矩阵的基本运算规则,包括矩阵的加法、乘法等,能够运用数组准确表示矩阵并实现相关运算,提升对数据结构中基础内容的应用能力。
  2. 全面掌握稀疏矩阵的三元组表示方法,清晰理解其存储原理,包括非零元素的行、列和值如何在三元组中记录,能够灵活运用该表示方法对稀疏矩阵进行高效存储与操作。
  3. 熟练掌握基于稀疏矩阵三元组表示的各种运算,如矩阵的转置、加法、乘法等,通过实际编程实现这些运算,深入体会稀疏矩阵在节省存储空间和提高运算效率方面的优势,增强对复杂数据结构运算的编程实现能力。

二,问题描述

设有两个6×6稀疏矩阵A、B如下:

编程实现稀疏矩阵的下列运算:

  • 向稀疏矩阵三元组插入一个新元素;
  • 求两个稀疏矩阵相加:C = A + B;
  • 求两个稀疏矩阵相乘:C = A × B;
  • 求稀疏矩阵A的转置矩阵AT。

三,基本要求

  • 设计稀疏矩阵三元组表示法的存储结构;
  • 设计基于三元组的稀疏矩阵的插入、创建及输出算法;
  • 设计基于三元组表示的稀疏矩阵的相加、相乘及求转置的算法;
  • 设计求解稀疏矩阵各种运算问题的完整程序,设计测试数据,上机调试、测试,保存和打印测试结果,对结果进行分析;
  • 掌握本实验中的各种算法。改写主函数,改用菜单控制方式完成本实验。

四、算法分析

(一)稀疏矩阵三元组表示法存储结构

采用结构体来定义三元组存储结构,结构体包含行索引、列索引和元素值三个成员,同时为了方便记录矩阵的行数、列数和非零元素个数,可再设计一个结构体来管理三元组数组。例如:

typedef struct {int row;int col;int value;
} Triple;typedef struct {Triple data[MAXSIZE];int rows;int cols;int num;
} SparseMatrix;

这种结构可以高效地存储稀疏矩阵,只记录非零元素的相关信息,大大节省存储空间。

(二)插入算法

遍历三元组数组,根据新元素的行索引和列索引确定插入位置。如果存在相同位置的元素,则更新其值;否则,将后续元素后移,插入新元素,并更新矩阵的非零元素个数。时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是三元组数组中元素的个数,因为在最坏情况下需要遍历整个数组来确定插入位置。

(三)创建算法

从输入获取矩阵的行数、列数和非零元素个数,然后依次输入每个非零元素的行索引、列索引和值,按照插入算法的逻辑将元素插入到三元组数组中。时间复杂度取决于输入非零元素的个数,若有 m m m 个非零元素,则时间复杂度为 O ( m ) O(m) O(m)

(四)输出算法

遍历三元组数组,按照矩阵的格式输出非零元素,对于非非零元素位置输出0。时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是三元组数组中元素的个数。

(五)相加算法

同时遍历两个稀疏矩阵的三元组数组,根据行索引和列索引判断元素是否对应。若对应,则将元素值相加,若结果不为0则存入结果矩阵的三元组数组;若不对应,则将较小索引对应的元素直接存入结果矩阵。时间复杂度为 O ( m + n ) O(m + n) O(m+n),其中 m m m n n n 分别是两个稀疏矩阵三元组数组中元素的个数。

(六)相乘算法

对于矩阵A的每一个非零元素,遍历矩阵B中相同列索引(即A元素的列索引等于B元素的行索引)的非零元素,将它们的值相乘并累加到结果矩阵对应位置。时间复杂度较高,为 O ( m × n × p ) O(m \times n \times p) O(m×n×p),其中 m m m n n n p p p 分别是矩阵A、B三元组数组中元素的个数以及结果矩阵可能的非零元素个数。

(七)转置算法

交换原矩阵三元组数组中元素的行索引和列索引,同时更新矩阵的行数和列数。为了保持三元组数组按行优先顺序存储,需要对交换后的数组进行重新排序。时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n n n 是三元组数组中元素的个数,主要是排序操作带来的时间消耗。

五,实验操作

1,双击Visual Studio程序快捷图标,启动程序。
在这里插入图片描述

2,之前创建过项目的话,直接打开即可,这里选择【创建新项目】。
在这里插入图片描述

3,单击选择【空项目】——单击【下一步】按钮。
在这里插入图片描述

4,编辑好项目的名称和存放路径,然后单击【创建】按钮。
在这里插入图片描述

5,创建C++程序文件,右击【源文件】——选择【添加】——【新建项】。
在这里插入图片描述
6,输入项目名称,单击【添加】按钮。
在这里插入图片描述

7,编写代码,单击运行按钮,运行程序。
在这里插入图片描述

六,示例代码

#include <iostream>
using namespace std;#define MAXSIZE 100 // 假设的最大非零元个数
typedef int ElemType;
typedef struct {int i; // 行int j; // 列ElemType v; // 值
} Triple;
typedef struct {Triple data[MAXSIZE + 1]; // 0下标不用int mu; // 行数int nu; // 列数int tu; // 非零元个数
} Tripletable;// 状态码定义
#define OK 1
#define ERROR 0
typedef int Status;// (1)稀疏矩阵三元组的插入运算
Status InsertTriple(Tripletable& M, int i, int j, ElemType v)
{// 将一个三元组(i, j, v)插入到稀疏矩阵三元组M中,插入后的M按行、列有序int k;if (i < 1 || j < 1 || M.tu >= MAXSIZE) return ERROR; // M.data的0下标不用for (k = M.tu; k > 0; k--) // 从M.tu到插入点之间的三元组逆序依次向后移动一个位置if (M.data[k].i > i || (M.data[k].i == i && M.data[k].j > j)){M.data[k + 1].i = M.data[k].i;M.data[k + 1].j = M.data[k].j;M.data[k + 1].v = M.data[k].v;}elsebreak;M.data[k + 1].i = i;M.data[k + 1].j = j;M.data[k + 1].v = v; // 插入M.tu++; // 调整参数return OK;
}// (2)创建稀疏矩阵三元组
void CreatTriple(Tripletable& M)
{int i, j, v, k, tu;do {printf("  输入稀疏矩阵的(行数,列数,非零元总数)=>");scanf("%d,%d,%d", &M.mu, &M.nu, &tu);} while (M.mu < 1 || M.nu < 1 || tu < 0);M.tu = 0;for (k = 1; k <= tu; k++){do {printf("  输入第%d个非零元素的(行≥1,列≥1,值≠0)=>", k);scanf("%d,%d,%d", &i, &j, &v);} while (i < 1 || j < 1 || v == 0);InsertTriple(M, i, j, v); // 调用插入算法创建稀疏矩阵三元组}
}// (3)输出稀疏矩阵三元组
void PrintTriple(Tripletable M)
{int ia;printf("%4d%4d%4d\n", M.mu, M.nu, M.tu);printf("===============\n");for (ia = 1; ia <= M.tu; ia++)printf("%4d%4d%4d\n", M.data[ia].i, M.data[ia].j, M.data[ia].v);
}// (4)两个稀疏矩阵相加
Status TripleAdd(Tripletable MA, Tripletable MB, Tripletable& MC)
{if (MA.tu + MB.tu >= MAXSIZE) // 溢出判断return ERROR;int ia = 1, ib = 1, ic = 1, k, temp;while (ia <= MA.tu && ib <= MB.tu){if (MA.data[ia].i < MB.data[ib].i || (MA.data[ia].i == MB.data[ib].i && MA.data[ia].j < MB.data[ib].j)){// 若MA中元素的下标(i, j)更小,// 则将MA中的该元素复制到MC中MC.data[ic].i = MA.data[ia].i;MC.data[ic].j = MA.data[ia].j;MC.data[ic++].v = MA.data[ia++].v;}else if (MA.data[ia].i == MB.data[ib].i && MA.data[ia].j == MB.data[ib].j){// 若MA与MB中元素的下标(i, j)相等,则值相加temp = MA.data[ia].v + MB.data[ib].v;if (temp != 0){MC.data[ic].i = MA.data[ia].i;MC.data[ic].j = MA.data[ia++].j;MC.data[ic++].v = temp;}else{ // 若两个元素值相加结果得0,则MA、MB同时取下一个元素ia++;ib++;}}else{ // 否则,MB的下标更小,取MB中的元素到MCMC.data[ic].i = MB.data[ib].i;MC.data[ic].j = MB.data[ib].j;MC.data[ic++].v = MB.data[ib++].v;}}for (k = ia; k <= MA.tu; k++) // 将MA中剩余的三元组复制到MC的尾部{MC.data[ic].i = MA.data[k].i;MC.data[ic].j = MA.data[k].j;MC.data[ic++].v = MA.data[k].v;}for (k = ib; k <= MB.tu; k++) // 将MB中剩余的三元组复制到MC的尾部{MC.data[ic].i = MB.data[k].i;MC.data[ic].j = MB.data[k].j;MC.data[ic++].v = MB.data[k].v;}MC.mu = MA.mu; // 确定MC的行、列数及非零元总数MC.nu = MA.nu;MC.tu = ic - 1;return OK;
}// (5)两个稀疏矩阵相乘
int TripleValue(Tripletable M, int i, int j)
{ // 在M中取(i, j)下标对应的元素值int k;for (k = 1; k <= M.tu; k++)if (M.data[k].i == i && M.data[k].j == j) // 若下标i, j与第k个三元组的i, j对应相等return (M.data[k].v); // 则返回该三元组的元素值,否则返回0return 0;
}
Status TripleMul(Tripletable A, Tripletable B, Tripletable& C)
{ // C=A×Bint i, j, k, p = 1, s; // p用于统计C的非零元个数if (A.nu != B.mu) return ERROR; // 矩阵相乘应满足的条件for (i = 0; i < A.mu; i++){for (j = 0; j < B.nu; j++){s = 0;for (k = 0; k < A.nu; k++){s = s + TripleValue(A, i, k) * TripleValue(B, k, j); // 计算}if (s != 0){C.data[p].i = i;C.data[p].j = j;C.data[p].v = s;p++;}}}C.mu = A.mu; // 确定C矩阵的行、列数及非零元总数C.nu = B.nu;C.tu = p - 1;return OK;
}// (6)求转置矩阵
void TranMat(Tripletable& AT, Tripletable A)
{int col, p, q, num[MAXSIZE], cpot[MAXSIZE];AT.mu = A.nu;AT.nu = A.mu;AT.tu = A.tu;for (col = 1; col <= A.nu; col++) num[col] = 0;for (p = 1; p <= A.tu; ++p) // 计算A中每列非零元的个数num[A.data[p].j]++;cpot[1] = 1;for (col = 2; col <= A.nu; col++) // 计算转置后元素的位置cpot[col] = cpot[col - 1] + num[col - 1];for (p = 1; p <= A.tu; ++p) // 转置{col = A.data[p].j;q = cpot[col];AT.data[q].i = A.data[p].j;AT.data[q].j = A.data[p].i;AT.data[q].v = A.data[p].v;cpot[col]++;}
}// 主函数设计
int main()
{Tripletable A, B, C, AT;printf("(1) 创建稀疏矩阵A的三元组:\n");CreatTriple(A);printf("A的三元组输出:\n");PrintTriple(A);printf("(2) 创建稀疏矩阵B的三元组:\n");CreatTriple(B);printf("B的三元组输出:\n");PrintTriple(B);printf("(3) 求C=A+B。\n");if (A.mu == B.mu && A.nu == B.nu){TripleAdd(A, B, C);printf("C的三元组输出:\n");PrintTriple(C);}elseprintf("A、B不能相加!\n");printf("(4) 求C=A×B。\n");if (A.nu == B.mu){TripleMul(A, B, C);printf("C的三元组输出:\n");PrintTriple(C);}elseprintf("A、B不能相乘!\n");printf("(5) 求A的转置矩阵AT。AT的三元组输出:\n");TranMat(AT, A);PrintTriple(AT);return 0;
}

七,运行效果

1,实验要求效果。
在这里插入图片描述

2,编写程序运行后的效果。
在这里插入图片描述

相关文章:

  • C++ STL 环形队列模拟实现
  • 解决Windows update服务启动拒绝访问的问题 | wuauserv 注册表拒绝访问的方法
  • CF148D Bag of mice
  • M|触碰你
  • [Android] 豆包爱学v4.5.0小学到研究生 题目Ai解析
  • 四月下旬系列
  • 在VMware Workstation 17 Pro上实现Windows与UOS虚拟机之间复制粘贴文本及文件
  • MySQL——事务
  • Linux中的软件管理
  • 【刷题Day20】TCP和UDP(浅)
  • 前端面试的话术集锦第 25 篇博文——CSS面试题上
  • 数仓面试内容
  • Windows系统安装`face_recognition`
  • 7.vtk坐标系
  • AI 提示词教程:从新手到高手的进阶之路
  • 运筹学之模拟退火
  • Python实例题:神经网络实现人脸识别任务
  • c++题目_P1443 马的遍历
  • [数学] 挑战nbc
  • WinForms开发基础:实现带X按钮的ClearableTextBox控件
  • 多元布局、抱团取暖……上海这个区和外向型企业坐到一起聊了什么
  • 圆桌|耐心资本对科技创新有何意义?天使投资最关注哪些要素?
  • 由“环滁皆山”到“环滁皆景”,滁州如何勾勒“文旅复兴”
  • 女子报警称醉酒后疑似被性侵,长沙警方:嫌犯邱某某已被刑拘
  • 北京:义务教育阶段入学将积极为多孩家庭长幼随学创造条件
  • 凭春晚分会场爆火的无锡,为何请来了上海主流媒体和网络大V