前言

本次练习的内容是对称矩阵的压缩储存以及各种配套函数的实现,先放一下对称矩阵的定义:

对于一个方阵A,若其中的元素满足,则称其为对称矩阵。通俗地理解,对称矩阵就是沿着主对角线(“\”这样的是主对角线)将矩阵折叠后对应元素相同的矩阵。

对称矩阵里有近一半的元素是相同的,如果对其进行压缩储存,我们就能省下近一半的空间。

练习目标

  1. 实现对称矩阵的压缩储存
  2. 实现相应的初始化、销毁、打印矩阵等函数
  3. 实现对压缩后的矩阵的值的修改

结构定义

首先先定义一个结构体:

#define dimSize 5
typedef struct SymmetricMatrix
{int *data;int dim; //dimension -> means dim*dim matrixSymmetricMatrix() : data(NULL), dim(-1) {}
} Mat;

取别名为Mat类型,其中dim意为方阵的维度,即表示为dim*dim阶方阵。构造函数里简单地做了一下初始化,另外,我们约定矩阵以行优先形式储存,并且储存下三角的元素+主对角线上的元素。

网上偷的示意图

辅助函数的实现

//创建Mat,其中数组的大小根据公式计算得出
Mat *createMat()
{Mat *mat = new Mat;mat->dim = dimSize;mat->data = new int[dimSize * (dimSize + 1) / 2]{0};return mat;
}//销毁Mat同时以引用形式将指针置空
void destroyMat(Mat *&mat)
{delete[] mat->data;delete mat;mat = NULL;
}//获取实际的数组长度,根据公式计算得出
int getArraySize(Mat *mat)
{int dim = mat->dim;int sz = dim * (dim + 1) / 2;return sz;
}//输出数组,做了一些简单的格式处理
void printArr(Mat *mat)
{int sz = getArraySize(mat);cout << "SMat:[";for (int i = 0; i < sz; i++){cout << mat->data[i];if (i != sz - 1)cout << " ";}cout << "] " << sz << endl;
}//为后面检查行列是否合法准备
bool checkIndex(Mat *mat, int col, int row)
{int n = mat->dim;if (col > n || row > n)return false;return true;
}

其中值得一提的是计算数组大小的公式 dim*(dim+1)/2,看上面给的红格子示意图,如果我们将每行的红色格子数量标出来,就能得到一个1 2 3 4 5的数列,根据高中的等差数列求和公式,化简后就可以得到dim*(dim+1)/2,这就是求压缩后的矩阵的实际数组长度的公式

实现根据数组数据初始化的函数

实现的是根据传进来的n*n大小的数组matrix来初始化压缩矩阵mat的函数,上代码:

void initMat(Mat *mat, int *matrix)
{int n = mat->dim;int k = 0;for (int i = 0; i < n; i++)//行for (int j = 0; j < i + 1; j++)//列if (j <= i)mat->data[k++] = matrix[i * n + j];
}

参数中的matrix指针实际上是一个n*n大小的矩阵数组,这里为了方便直接使用了一维数组,其实按常规思维的话要用二维数组来表示矩阵。

这几行代码的核心思路是遍历传进来的数组,将下三角+主对角线上的元素存进数组里。其中k代表mat中数组的下标,i*n+j是用来分割数组的,因为传进来的是用一维数组储存的矩阵,i为行数j为列数。

除此之外,第二层循环的条件j < i+1是根据情况优化的,因为对于矩阵压缩而言,每行只储存和行数相同数量的元素,同时考虑到数组下标是从0开始,所以这里就写成j<i+1;循环最内层的j<=i判断是因为对于下三角元素和主对角线元素而言,他们的列号永远小于等于行号,所以就可以根据这个条件来筛选哪些元素是需要存的,哪些是可以压缩掉不存的。

实现在压缩矩阵里取值的函数

先上代码:

int getValue(Mat *mat, int col, int row) //Columns and rows are logical index (based-1)
{if (!checkIndex(mat, col, row))return INT_MIN;if (col > row)                         //target value is located in upper triangular matrixswap(col, row);                    //turn the indices into lower triangular matrixint i = row * (row - 1) / 2 + col - 1; //logical index to physical indexreturn mat->data[i];
}

代码中写了一些英文注释帮助理解。参数中的col和row就是要取的值的列号和行号,是从1开始的;checkIndex在上面辅助函数里有,不再赘述。重点解释一下第二个if和i的计算:

1.函数中第二个if是为了应对取上三角元素的情况(列号大于行号,即是上三角元素),因为在数组中我们没有存上三角元素,所以这里需要绕个弯,利用对称矩阵的特性(其实这就是为何能压缩的原因)将行列号互换就变成了取下三角元素,就可以直接在数组里取值。

2.i的计算其实也是和之前算数组长度的原理差不多,不过这里的参数是从1开始的行列号,所以要同时把逻辑下标转成物理下标才行。如果不用转换成物理下标的话,我们的公式应该是这样的 i = row*(row+1)/2 + col,大概的原理就是先计算当前元素上面有多少行的元素,再加上当前行的当前元素前面有多少个元素,加起来就能得到当前元素在数组的位置。但是现在我们需要转换成物理下标,所以就要把行号和列号都减1,公式就变成了i = row*(row-1)/2 + col - 1

如果还是不能理解的话推荐自行画图并结合等差数列求和公式推导一遍。

实现打印矩阵的函数

因为有了上面实现的getValue函数,打印矩阵的函数很快就能写出来:

void printMat(Mat *mat)
{int n = mat->dim;for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++)cout << getValue(mat, i, j) << " ";cout << endl;}
}

注意getValue接收的行列号是逻辑下标,即从1开始。

实现修改矩阵元素的值的函数

根据上面的公式,我们很快就能实现这个函数:

void value(Mat *mat, int val, int col, int row)
{if (!checkIndex(mat, col, row))return;if (col > row)swap(col, row);int i = row * (row - 1) / 2 + col - 1;mat->data[i] = val;
}

要注意的是如果我们修改一个主对角线以外的元素,会同时修改他对称的另一个元素,即修改a12会同时修改a21。

完整代码

给了main函数和测试的样例,直接运行即可。

#include <iostream>using namespace std;#define dimSize 5
typedef struct SymmetricMatrix
{int *data;int dim; //dimension -> means dim*dim matrixSymmetricMatrix() : data(NULL), dim(-1) {}
} Mat;Mat *createMat()
{Mat *mat = new Mat;mat->dim = dimSize;mat->data = new int[dimSize * (dimSize + 1) / 2]{0};return mat;
}void destroyMat(Mat *&mat)
{delete[] mat->data;delete mat;mat = NULL;
}int getArraySize(Mat *mat)
{int dim = mat->dim;int sz = dim * (dim + 1) / 2;return sz;
}void printArr(Mat *mat)
{int sz = getArraySize(mat);cout << "SMat:[";for (int i = 0; i < sz; i++){cout << mat->data[i];if (i != sz - 1)cout << " ";}cout << "] " << sz << endl;
}bool checkIndex(Mat *mat, int col, int row)
{int n = mat->dim;if (col > n || row > n)return false;return true;
}void initMat(Mat *mat, int *matrix)
{int n = mat->dim;int k = 0;for (int i = 0; i < n; i++)for (int j = 0; j < i + 1; j++)if (j <= i)mat->data[k++] = matrix[j * n + i];
}int getValue(Mat *mat, int col, int row) //Columns and rows are logical index (based-1)
{if (!checkIndex(mat, col, row))return INT_MIN;if (col > row)                         //target value is located in upper triangular matrixswap(col, row);                    //turn the indices into lower triangular matrixint i = row * (row - 1) / 2 + col - 1; //logical index to physical indexreturn mat->data[i];
}void value(Mat *mat, int val, int col, int row)
{if (!checkIndex(mat, col, row))return;if (col > row)swap(col, row);int i = row * (row - 1) / 2 + col - 1;mat->data[i] = val;
}void printMat(Mat *mat)
{int n = mat->dim;for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++)cout << getValue(mat, i, j) << " ";cout << endl;}
}int main()
{Mat *mat = createMat();int arr[25]{1, 2, 3, 4, 5,2, 2, 7, 7, 5,3, 7, 4, 2, 5,4, 7, 2, 1, 3,5, 5, 5, 3, 8};initMat(mat, arr);printArr(mat);printMat(mat);cout << endl;value(mat, 99, 4, 1);printMat(mat);destroyMat(mat);return 0;
}

总结

对称矩阵的压缩的实现难度比较小,重难点在于下标的计算,下标的计算公式用到了等差数列的求和公式。万丈高楼平地起,越是基础的东西越需要多打多练。

顺序表练习(三):对称矩阵的压缩储存相关推荐

  1. 顺序表基本操作<小白一听就懂!!!><超详细><接地气>

    顺序表基本操作<小白一听就懂!!!><超详细>&&<接地气> ***小编前言*** 完整代码 // 头文件 //主函数 // 初始化 /*运用指针* ...

  2. 【数据结构】顺序表(C++)

    顺序表 顺序表 图示 代码实现 实际应用 顺序表 顺序表是简单的一种线性结构,逻辑上相邻的数据在计算机中内的存储位置也是相邻的,可以快速定位第几个元素,中间允许有空值,插入.删除时需要移动大量元素. ...

  3. 《数据结构》顺序表与链表

    目录 一.线性表 线性表简介 二.顺序表 1.定义顺序表类 2.功能实现: 打印顺序表 在pos位置新增元素 判定是否包含某个元素 查找某个元素对应的位置 获取pos位置的元素 给 pos 位置的元素 ...

  4. 数据结构25 ————顺序表查找

    数据结构25 ----顺序表查找 一. 目录 文章目录 数据结构25 ----顺序表查找 一. 目录 二. 顺序表查找 三. 顺序表查找代码 1.基本算法 2.进行优化 四. 参考资料 二. 顺序表查 ...

  5. 【数据结构】顺序表的增删查改 (C语言实现)

    文章目录 一.线性表 二.顺序表 1.什么是顺序表 2.顺序表的分类 三.动态顺序表的实现 1.结构的定义 2.顺序表的初始化 3.检查容量 4.在头部插入数据 5.在尾部插入数据 6.在指定位置插入 ...

  6. 一篇看懂顺序表!!(刘欣大佬《码农翻身》特别提及)

    顺序表 1.1定义 顺序表是用一段物理地址(内存)连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储.在数组上完成数   据的增删查改. 1.2分类 顺序表一般可以分为: 静态顺序表:使 ...

  7. 数组:矩阵快速转置 矩阵相加 三元组顺序表/三元矩阵 随机生成稀疏矩阵 压缩矩阵【C语言,数据结构】(内含源代码)

    目录 题目: 题目分析: 概要设计: 二维矩阵数据结构: 三元数组\三元顺序表顺序表结构: 详细设计: 三元矩阵相加: 三元矩阵快速转置: 调试分析: 用户手册: 测试结果: 源代码: 主程序: 头文 ...

  8. 行逻辑链接的顺序表(压缩存储稀疏矩阵)详解

    前面学习了如何使用三元组顺序表存储稀疏矩阵,其实现过程就是将矩阵中各个非 0 元素的行标.列标和元素值以三元组的形式存储到一维数组中.通过研究实现代码你会发现,三元组顺序表每次提取指定元素都需要遍历整 ...

  9. 数据结构线性表的逻辑结构(三)顺序表基本操作的实现

    一. 实验目的 1. 掌握线性表的逻辑结构: 2. 顺序表基本操作的实现: 3. 掌握利用C/C++编程语言实现数据结构的编程方法: 4. 通过上机时间加强利用数据结构解决实际应用问题的能力: 二.  ...

最新文章

  1. RPC 笔记(06)— socket 通信(多线程服务器)
  2. 前端开发的难点到底在什么地方?
  3. [转载]如何高效、轻松地利用一天?
  4. 汽车智力游戏-汽车游戏大全
  5. python 分类 投票_使用python+redis实现文章发布,投票,分组排名功能
  6. HardDisk读取速度
  7. 用html制作ps,ps制作图片的步骤
  8. 计算机专业sci二区论文难吗,二区SCI论文要求比国内核心高吗
  9. H3C交换机IPv6无状态地址自动配置
  10. 在python中month函数的用法_python @classmethod 的使用场合
  11. IOS中使用getUserMedia获取视频流展示到video中,进行人脸识别
  12. kafka sasl java_Kafka安装及开启SASL_PLAINTEXT认证(用户名和密码认证)
  13. linux设置小技巧
  14. Python基础07
  15. 粉屏门又一次显示出了苹果的傲慢
  16. 路由器经常出现断线现象解决对策在哪
  17. 2021-2027全球与中国医疗物联网服务市场现状及未来发展趋势
  18. python中map函数返回值类型_Python学习第42课-map()函数
  19. zsh与oh-my-zsh ← 阳志平的个人网站::技术 zsh与oh-my-zsh ← 阳志平的个人网站::技术...
  20. [机缘参悟-77]:深度思考-《天道》中强势文化、弱势文化与人的行为模式的关系

热门文章

  1. 谷歌浏览器Chrome 100即将发布,但可能无法正常使用?
  2. 项目管理:项目开发类型、模型、流程以及案例介绍
  3. Python-List
  4. Python读取显示raw图片+numpy基本用法记录
  5. html5是什么语言,html5 css3是什么?
  6. 微擎微信公众号消息模板
  7. ubuntu和windows之间实现复制粘贴
  8. google的秘密入口+搜索技巧
  9. WPF实现照片墙拼图展示特效
  10. linux下ps ef命令详解,linux命令中ps -ef详解