数据结构——第4章 数组与广义表(2)

矩阵

  • 1.特殊矩阵的压缩存储
    • 1.1对称矩阵
    • 1.2.三角矩阵
      • 1.2.1.下三角矩阵
      • 1.2.2. 上三角矩阵
    • 1.3 带状矩阵
  • 2.稀疏矩阵
    • 2.1. 稀疏矩阵的三元组表存储
      • 2.1.1. 稀疏矩阵的转置

1.特殊矩阵的压缩存储

矩阵与二维数组具有很好的对应关系,因此在进行科学计算时,常常用二维数组存储数学上的矩阵。但是在实际问题中,从数学模型中抽象出来的矩阵是一种特殊矩阵,如三角矩阵、对称矩阵、带状矩阵和稀疏矩阵,必然造成空间的浪费。本节从节约存储空间的角度考虑,研究这些特殊矩阵的存储方式。

1.1对称矩阵

对称矩阵的特点是:在一个n阶方阵中,有aij=aji,其中0 <= i,j <= n-1。由于对称矩阵关于主对角线对称,因此只需存储上三角或下三角部分即可。比如,只存储下三角中的元素aij,其特点是j<=i且0<=i<n-1;对于上三角中的元素aij,它和对应的aji相等。因此当访问的元素位于上三角时,直接去访问和它对应的下三角元素即可。这样,原来需要n * n个存储单元,现在只需要n(n+1)/2个存储单元,当n较大时,就可以可省相当可观的一部分存储资源。
对于任意的n阶对称矩阵,要存储其下三角部分,通常的方法是将下三角部分的所有元素以行为主序顺序存储到一个一维数组中去。在下三角中共有n(n+1)/2个元素,存储到一维数组sa[n(n+1)/2]中。此时ij对应数组的关系是怎么样呢?
对于下三角中元素aij,其特点是:j<=i且0<=i<=n-1。存储到sa中,根据存储原则,它前面有i行,共有1+2+……+i=i *(i+1)/2,因此它在sa中的下标k与i,j的关系如下:
在这里插入图片描述
若i<j,则aij是上三角中的元素,因为aij=aji。这样,访问上三角中aij时直接访问和它对应的下三角中的aji即可,在sa中的对应位置如下:
在这里插入图片描述
综上所述,对于对称矩阵中的任意元素aij,若令i=max(i,j),j=min(i,j),则将上两个式子综合起来即可得到:k=i * (i+1)/2 +j。

//创建对称矩阵
void symmetry_matrix(int A[], int n)
{
	int i, j;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j <= i; j++)
		{
			printf("请输入第%d行的%d个元素\n", i + 1, j + 1);
			scanf("%d", &A[i * (i + 1) / 2 + j]);
		}
	}
}

//显示对称矩阵
void disp_symmetry_matrix(int A[], int n)
{
	int i, j;
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < n; j++)
		{
			if (j <= i)
			{
				printf("%5d", A[i * (i + 1) / 2 + j]);
			}
			else
			{
				printf("%5d", A[j * (j + 1) / 2 + i]);
			}
			printf("\n");
		}
	}
}

1.2.三角矩阵

三角矩阵的特点是上三角或下三角是一个同常量c,下面讨论它们的压缩存储方法。

1.2.1.下三角矩阵

与对称矩阵类似,不同之处在于存完下三角中的所有元素之后,接着存储对角上方的常量c。因为是同一个常数,所有存储一个即可,这样一共需要存储
n(n+1)/2+1个元素。设存入一维数组sa[n(n+1)/2+1]时,这种存储方式可节约n(n-1)/2-1个存储单元。
sa[k]与a[i][j]的对应关系为:
在这里插入图片描述

1.2.2. 上三角矩阵

对于上三角矩阵,以行为主序存储上三角部分,最后存储对角线下方的常量c,
和下三角的i和j方向相反。

1.3 带状矩阵

对于n阶矩阵A,如果存在最小正数m,满足当|i-j|>=m时,aij=0,则称A为带状矩阵并且称w=2m-1为矩阵A的带宽。其特点是所有非零元素都集中在以主对角线为中心的带状区域中,除了主对角线和它的上下方若干条对角线的元素外,所有其他元素都为零。
带状矩阵A的压缩存储到一个n行w列的二维数组b中。那么A[i][j]映射为B[m][n],映射关系为:m=i,n=j-i+1 。

2.稀疏矩阵

设m * n矩阵中非零元素的分布没有规律,且非零元素个数为t(t<< m * n),则称该矩阵为稀疏矩阵。一种有效的解决方案是:对于每一个非零元素,不仅存储元素值,还存储它所在的行和列,即将非零元素所在的行、列及值构成一个三元组(i,j,v),然后再按某种规律存储这些三元组。

2.1. 稀疏矩阵的三元组表存储

将稀疏矩阵中的三元组按行优先的顺序以及同一行中列号从小到大的规律排列成一个线性表,称为三元组表。
显然,要唯一低表示一个稀疏矩阵,在存储空间三元组表的同时还需要存储行数和列数。此外,为了运算方便,还要存储矩阵的非零元素的个数。

typedef struct SPNode
{
	int i, j;							//非零元素的行列
	DataType v;					//非零元素值
}SPNode;							//三元数组类型
typedef struct SPMatrix
{
	SPNode data[SMAX];	//三元组表
	int mu, nu, tu;				//矩阵的行、列及非零元素的个数
}SPMatrix;						//三元组表的存储类型

这样的存储方法极大地压缩了稀疏矩阵的存储空间,但是会使矩阵的运算变得复杂。下面讨论基于三元组存储方式的稀疏矩阵的两种运算:转置和相乘。

2.1.1. 稀疏矩阵的转置

设:SPMatrix TA,TB;
其中,TA表示一个m * n的稀疏矩阵对应存储的三元组表,TB对应的是其转置矩阵n * m的稀疏矩阵对应存储的三元组表。
根据转置算法可知,由TA求TB,需要进行如下操作:
(1)将TA的行、列转化成TB的列、行。
(2)将TA.data中每一个三元组的行列变换后存储到TB.data中.
需要注意的是,无论是TA还是TB,都以行优先进行存储 。如果依次取出TA中的每个三元组,转换为TB中的对应三元组,则无法确定TA.data[k]在TB中的存储位置。为此,按列序从TA中依次取出每一个三元组,并将其转换,便可对应于TB中按行存储的三元组。

void TransM1(SPMatrix TA, SPMatrix* TB)
{
	int p, q, col;
	//稀疏矩阵的行、列和元素个数
	TB->mu = TA.nu;
	TB->nu = TA.mu;
	TB->tu = TA.tu;
	if (TB->tu > 0)				//有非零元素则转换
	{
		q = 0;
		for (col = 0; col < (TA.nu); col++)
		{
			for (p = 0; p < (TA.tu); p++)//扫描整个三元组表
			{
				if (TA.data[p].j == col)
				{
					TB->data[q].i = TA.data[p].j;
					TB->data[q].j = TA.data[p].i;
					TB->data[q].v = TA.data[p].v;
					q++;
				}
			}
		}
	}
}

【算法分析】
该算法的时间主要耗费在col和p的二重循环上,所以时间复杂度为O(n * t)(设m、n是原矩阵的行、列,t是稀疏矩阵的非零元素个数),显然当非零元素的个数t和 m * n数量极同时,算法的时间复杂度为O(m * n * n),和通常存储方式的矩阵装置算法相比,可能节约了一定量的存储空间,但算法的时间性能更差一些。

算法一效率低的原因是,要从TA的三元组表中寻找第0列、第1列……需反复搜索TA表,若能直接确定TA中每一个三元组在TB中的位置,则对TA的三元表扫描一次即可。


void TransM2(SPMatrix TA, SPMatrix* TB)
{
	int i, j, k;
	int num[SMAX] = { 0 };
	int cpot[SMAX] = { 0 };
	//稀疏矩阵的行、列和元素个数
	TB->mu = TA.nu;
	TB->nu = TA.mu;
	TB->tu = TA.tu;
	if (TB->tu > 0)
	{
		for (i = 0; i < TA.tu; i++)
		{
			j = TA.data[i].j;
			num[j]++;
		}
		for (i = 1; i < TA.nu; i++)
		{
			cpot[i] = cpot[i - 1] + num[i - 1];
		}
		for (i = 0; i < TA.tu; i++)
		{
			j = TA.data[i].j;
			k = cpot[j];
			TB->data[k].i = TA.data[i].j;
			TB->data[k].j = TA.data[i].i;
			TB->data[k].v = TA.data[i].v;
			cpot[j]++;
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/25.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

两年外包生涯做完,感觉自己废了一半....

先说一下自己的情况。大专生&#xff0c;17年通过校招进入湖南某软件公司&#xff0c;干了接近2年的点点点&#xff0c;今年年上旬&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落&#xff01;而我已经在一个企业干了五年的功能测试…

Leetcode. 88合并两个有序数组

合并两个有序数组 文章目录归并思路二归并 核心思路&#xff1a; 依次比较&#xff0c;取较小值放入新数组中 i 遍历nums1 &#xff0c; j 遍历nums2 &#xff0c;取较小值放入nums3中 那如果nums[i] 和nums[j]中相等&#xff0c;随便放一个到nums3 那如果nums[i] 和nums[j]中相…

Form Generator扩展 文本 组件

一、form-generator是什么?✨ ⭐️ 🌟 form-generator的作者是这样介绍的:Element UI表单设计及代码生成器,可将生成的代码直接运行在基于Element的vue项目中;也可导出JSON表单,使用配套的解析器将JSON解析成真实的表单。 但目前它提供的组件并不能满足我们在项目中的…

2023年再不会Redis,就要被淘汰了

目录专栏导读一、同样是缓存&#xff0c;用map不行吗&#xff1f;二、Redis为什么是单线程的&#xff1f;三、Redis真的是单线程的吗&#xff1f;四、Redis优缺点1、优点2、缺点五、Redis常见业务场景六、Redis常见数据类型1、String2、List3、Hash4、Set5、Zset6、BitMap7、Bi…

英雄算法学习路线

文章目录零、自我介绍一、关于拜师二、关于编程语言三、算法学习路线1、算法集训1&#xff09;九日集训2&#xff09;每月算法集训2、算法专栏3、算法总包四、英雄算法联盟1、英雄算法联盟是什么&#xff1f;2、如何加入英雄算法联盟&#xff1f;3、为何会有英雄算法联盟&#…

【Linux修炼】16.共享内存

每一个不曾起舞的日子&#xff0c;都是对生命的辜负。 共享内存一.共享内存的原理二.共享内存你的概念2.1 接口认识2.2演示生成key的唯一性2.3 再谈key三.共享资源的查看3.1 如何查看IPC资源3.2 IPC资源的特征3.3 进程之间通过共享内存进行关联四.共享内存的特点五.共享内存的内…

10个最频繁用于解释机器学习模型的 Python 库

文章目录什么是XAI&#xff1f;可解释性实践的步骤技术交流1、SHAP2、LIME3、Eli54、Shapash5、Anchors6、BreakDown7、Interpret-Text8、aix360 (AI Explainability 360)9、OmniXAI10、XAI (eXplainable AI)XAI的目标是为模型的行为和决定提供有意义的解释&#xff0c;本文整理…

C++基础算法①——高精度加减法计算

高精度算法1.导论2.高精度低精度3.高精度高精度4.高精度减法1.导论 当我们利用计算机进行数值计算&#xff0c;有时候会遇到这样的问题&#xff1a; n&#xff01;的精确结果是多少&#xff1f; 当n小于30的时候&#xff0c;我们当然可以通过电脑自带的计算器计算出来。但是当…

「Vue面试题」动态给vue的data添加一个新的属性时会发生什么?怎样去解决的?

一、直接添加属性的问题 我们从一个例子开始 定义一个p标签&#xff0c;通过v-for指令进行遍历 然后给botton标签绑定点击事件&#xff0c;我们预期点击按钮时&#xff0c;数据新增一个属性&#xff0c;界面也 新增一行 <p v-for"(value,key) in item" :key&q…

学习 Python 之 Pygame 开发坦克大战(一)

学习 Python 之 Pygame 开发坦克大战&#xff08;一&#xff09;Pygame什么是Pygame?初识pygame1. 使用pygame创建窗口2. 设置窗口背景颜色3. 获取窗口中的事件4. 在窗口中展示图片(1). pygame中的直角坐标系(2). 展示图片(3). 给部分区域设置颜色5. 在窗口中显示文字6. 播放音…

如何在CSDN中使用ChatGPT

本篇文章致力于帮助大家理解和使用ChatGPT&#xff08;现在CSDN改成”C知道“了&#xff09;。简介ChatGPT是OpenAI公司开发的一种大型语言模型。它是一种基于Transformer架构的深度学习模型&#xff0c;可以对语言进行建模和生成。它可以处理问答、对话生成、文本生成等多种任…

1.认识网络爬虫

1.认识网络爬虫网络爬虫爬虫的合法性HTTP协议请求与响应(重点)网络爬虫 爬虫的全名叫网络爬虫&#xff0c;简称爬虫。他还有其他的名字&#xff0c;比如网络机器人&#xff0c;网络蜘蛛等等。爬虫就好像一个探测机器&#xff0c;它的基本操作就是模拟人的行为去各个网站溜达&am…

TCP/IP协议

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录TCP/IP协议应用层协议自定义应用层协议DNS传输层协议端口号UDP协议UDP协议端格式TCP协议TCP协议段格式TCP工作机制确认应答(安…

单片机怎么实现真正的多线程?

所谓多线程都是模拟的&#xff0c;本质都是单线程&#xff0c;因为cpu同一时刻只能执行一段代码。模拟的多线程就是任务之间快速切换&#xff0c;看起来像同时执行的样子。据说最近有多核的单片机&#xff0c;不过成本应该会高很多。对于模拟的多线程&#xff0c;我知道的有两种…

html实现浪漫的爱情日记(附源码)

文章目录1.设计来源1.1 主界面1.2 遇见1.3 相熟1.4 相知1.5 相念2.效果和源码2.1 动态效果2.2 源代码2.3 代码结构源码下载更多爱情表白源码作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/129264757 html实现浪漫的爱情…

【C++】红黑树

文章目录红黑树的概念红黑树的性质特征红黑树结点的定义红黑树的插入操作情况1情况2情况3特殊情况代码实现红黑树的验证红黑树的删除红黑树和AVL树的比较红黑树的应用红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但是每一个结点都增加一个存储位表示结点的颜…

把C#代码上传到NuGet,大佬竟是我自己!!!

背景 刚发表完一篇博客总结自己写标准化C#代码的心历路程&#xff0c;立马就产生一个问题&#xff0c;就是我写好标准化代码后&#xff0c;一直存放磁盘的话&#xff0c;随着年月增加&#xff0c;代码越来越多&#xff0c;项目和版本的管理就会成为一个令我十分头疼的难题&…

可路由计算引擎实现前置数据库

很多大机构都会有个中央数据仓库负责向应用提供数据服务。随着业务的发展&#xff0c;中央数据仓库的负载在持续增加。一方面&#xff0c;数仓是前端应用的数据后台&#xff0c;而前端应用不断增多&#xff0c;用户访问的并发数也不断增长。另一方面&#xff0c;数仓还要承担原…

ChatGPT在工业领域的用法

在工业数字化时代&#xff0c;我们需要怎么样的ChatGPT&#xff1f; 近日&#xff0c;ChatGPT热度高居不下&#xff0c;强大的人机交互能力令人咋舌&#xff0c;在国内更是掀起一股讨论热潮。一时间&#xff0c;这场由ChatGPT引起的科技飓风&#xff0c;使得全球最顶尖科技力量…

C++回顾(一)——从C到C++

前言 在学习了C语言的基础上&#xff0c;C到底和C有什么区别呢&#xff1f; 1.1 第一个C程序 #include <iostream>// 使用名为std的命名空间 using namespace std;int main() {// printf ("hello world\n");// cout 标准输出 往屏幕打印内容 相当于C语言的…