稀疏矩阵【快速转置法】【详解-C语言】

news2024/10/22 1:13:26

目录

  • 一、稀疏矩阵
  • 二、存储方法
    • 2.1三元组法
  • 三、稀疏矩阵转置
    • 3.1朴素思想
    • 3.2快速转置

一、稀疏矩阵

什么是稀疏矩阵?它没有确切的定义,一般是指矩阵中存储非零元素相对整个矩阵存储的总元素来说很少的矩阵称为稀疏矩阵,这个多少怎么看呢?计算矩阵的稀疏因 K = 非零元个数 矩阵全部元素个数 ≤ 0.05 K=\frac{非零元个数}{矩阵全部元素个数}\leq0.05 K=矩阵全部元素个数非零元个数0.05时称为稀疏矩阵

二、存储方法

由于稀疏矩阵中存在大量零元素,从而占用大量空间,如何对它进行压缩存储?所谓压缩存储是指只存储非零元的信息节省不必要的空间浪费。常用的压缩存储方法有:【三元组法】【十字链表法】

2.1三元组法

所谓三元组法是指用一块相同大小的连续的内存空间(顺序表)或者结点形式(链表),并以按矩阵的行顺序存储矩阵中的每个非零元信息。为确保非零元唯一确定,三元组保存信息时需要保存非零元的所在矩阵中的行号和列号以及元素本身,即一个三元组表示为:(i,j,aij),i是行号,j是列号。一个矩阵的所有非零元的三元组作为结构类型的元素存储在连续的内存空间中或链表形成一个集合称为三元组表。(注:按矩阵的行顺序存储是指矩阵的第一行的第一个非零元存储在三元组表的第一个位置,第二个非零元存储在三元组表的第二个位置
有了三元组表可以唯一确定矩阵中的非零元位置,但却不能唯一确定矩阵,因为矩阵的行数和列数没有指明,因此需要用额外的空间存储矩阵的列数和行数,为了方便再加一个空间来存储矩阵中非零元的总数,其存储方式如下:

//这里以顺序表存储为例,以三元组的方式存储稀疏矩阵
#define MAXSIZE 100
typedef int ElemType;//类型重命名【鉴名知意】
typedef struct {
	int i,j;//非零元的行和列
	ElemType e;//非零元
}Triple;

typedef struct{
	Triple data[MAXSIZE];
	int m,n,nonzeroNums;//矩阵的行m,矩阵的列n,非零元数量nonzeroNums
}TSMatrix

三、稀疏矩阵转置

矩阵的转置是指:由原来的矩阵Mmxn转换为另一个矩阵Tnxm,矩阵M中j行i列的位置的元素存储在矩阵T中i行j列位置上,表示为: T ( i , j ) = M ( j , i ) , 1 ≤ i ≤ n , 1 ≤ j ≤ n T(i,j)=M(j,i),1\leq i\leq n,1\leq j\leq n T(i,j)=M(j,i)1in1jn。这时称矩阵M与矩阵T互为转置矩阵。由其转置的定义可知,把一个矩阵转置需要如下步骤
① 将矩阵M的行数和列数交叉赋给矩阵T,矩阵M的行数和列数分别为矩阵T的列数和行数。
② 将每个三元组表中的三元组的i和j互换。
③ 重新排列三元组之间的次序(三元组法存储矩阵M时是按矩阵的行顺序存储的方式把非零元对应的三元组存储在三元组表中,而转置后非零元的行和列顺序改变了,因此要重新调整三元组表中三元组的次序)

3.1朴素思想

我们最终的目标是: 把转置矩阵的T的非零元按矩阵T的行顺序存储的方式把非零元对应的三元组存储在三元表中。由于矩阵M转置为T后,T的第一行元素对应是原来矩阵M的第一列元素,第二行对应M的第二列…,因此重新排列后的三元组表(转置矩阵T对应的三元组表)里的三元组会先存储的是矩阵M的第一列的非零元对应的三元组,然后是第二列的…直到最后一列。代码如下:

Status TransposeSMatrix(TSMatrix M,TsMatrix *T){
//在实际存储中,存储矩阵的二维数组一般是从0行、0列开始的,而不是和逻辑上的矩阵一样从1行1列开始
//当然也可以自定义存储矩阵的二维数组和逻辑的一致,从1行1列开始,下面是以0行0列开始的为例。
	//矩阵M的行、列数变为矩阵T的列、行数
	T->m=M.n;
	T->n=M.m;
	T->nonzeroNums=M.nonzeroNums;
	int col;//矩阵M的列
	int q=0;//转置后的三元组表的下标
	int p=0;//原三元组表的下标
	if(T.nonzeroNums){
		for(col=0;col<M.n;col++){//遍历矩阵M的每一列
			for(p=0;p<M.nonzeroNums;p++){
				if(M.data[p].j==col){
					T->data[q].i=M.data[p].j;
					T->data[q].j=M.data[p].i;
					T->data[q].e=M.data[p].e;
					q++;
				}
			}
		}
	}
	return OK;
}

时间复杂度:O(nnonzeroNums),n是指矩阵M的列数,nonzeroNums是指非零元总数。若一个矩阵中非零元的个数很多就会导致nnonzeroNums的数量级较大,例如,若在M100x500的矩阵中,非零元的数量是10000,虽然压缩了矩阵,但是矩阵转置的时间开销会很大,下面介绍一种快速转置的方法。

3.2快速转置

从上一节的转置的目标可知,转置矩阵T对应的三元组表里的三元组顺序实际就是矩阵转置前的矩阵M中按列遍历非零元的次数。假如M的第一列非零元有两个,则转置后,这两个非零元对应的三元组必定是排在矩阵T对应的三元组表的前两个位置,M第二列的非零元则排在第一列的非零元之后,第三列依次排在第二列的后面,从这里可以知道:若知道矩阵M中每一列的第一个非零元在”新“的三元组表中的位置(相当于把三元组表划分为n段,矩阵M每一列的第一个非零元存放在对应段的开始位置),那么在遍历”旧“的三元组表时,只需要把属于同一列的放在同一个段内即可。因此,需要两个一维数组的辅助空间num[],copt[]。

  • num[i] :表示存储矩阵M的i列的非零元个数
  • copt[i] :表示矩阵M的i列的第一个非零元在”新“三元组表中的位置

显然有:(注:数组、二维数组实际存储中,这里都已从0下标开始)
{ c o p t [ 0 ] = 0 第1列(下标col=0)的第一个非零元一定存放在”新“三元组表的第一个位置(下标为0) c o p t [ c o l ] = c o p t [ c o l − 1 ] + n u m [ c o l − 1 ] , 1 ≤ c o l < n o n z e r o N u m s , 每一列非零元在新三元组表中的位置都是基于前一列在三元组表中的位置的末尾,因此要加 n u m [ c o l − 1 ] \begin{cases} copt[0]=0 & \text {第1列(下标col=0)的第一个非零元一定存放在”新“三元组表的第一个位置(下标为0)} \\\\ copt[col]=copt[col-1]+num[col-1], & 1\leq col< nonzeroNums,每一列非零元在新三元组表中的位置都是基于前一列在三元组表中的位置的末尾,因此要加num[col-1] \end{cases} copt[0]=0copt[col]=copt[col1]+num[col1],1(下标col=0)的第一个非零元一定存放在三元组表的第一个位置(下标为0)1col<nonzeroNums,每一列非零元在新三元组表中的位置都是基于前一列在三元组表中的位置的末尾,因此要加num[col1]
对于同一列的非零元怎么处理?在遍历”旧“三元组表的元素(三元组)过程中,当头次遇到某列的第一个非零元并取其列标赋给col=M.data[p].j ,则去copt中获取该的列所在的段起始位置copt[col],并把该非零元存储到"新"三元组表的copt[col]位置上,然后让copt[col]++,即更新同一列所在的段的起始位置,当再次遇到同一列的非零元时,该非零元会放在上次该列的首个非零元的后边,然后再让copt[col]++…依次直到遍历完”旧三元组表“,转置结束。(备注:三元组是指存储非零元的行、列、非零元本身这样一个记录,我把非零元和三元组等价,指的是同一个意思,若无特指,非零元代表三元组;”旧“三元组表是指转置前矩阵M对应三元组表,”新“三元组表则是转置后矩阵T对应的三元组表,也是我们要的结果

案例(逻辑上):
M = ( 0 12 0 0 − 3 0 0 4 0 0 9 0 7 0 0 5 ) M=\begin{pmatrix} 0 & 12 & 0 &0 \\\\ -3 & 0 &0 &4 \\\\ 0&0&9&0 \\\\ 7 & 0 &0 &5 \\\\ \end{pmatrix} M= 03071200000900405 转置后的矩阵 T = ( 0 − 3 0 7 12 0 0 0 0 0 9 0 0 4 0 5 ) 转置后的矩阵T=\begin{pmatrix} 0 & -3 & 0 &7 \\\\ 12 & 0 &0 &0 \\\\ 0&0&9&0 \\\\ 0 & 4&0 &5 \\\\ \end{pmatrix} 转置后的矩阵T= 01200300400907005

旧三元组表:
[ ( 1 , 2 , 12 ) , ( 2 , 1 , − 3 ) , ( 2 , 4 , 4 ) , ( 3 , 3 , 9 ) , ( 4 , 1 , 7 ) , ( 4 , 4 , 5 ) ] [(1,2,12) ,(2,1,-3) ,(2,4,4) ,(3,3,9) ,(4,1,7) ,(4,4,5)] [(1,2,12),(2,1,3),(2,4,4),(3,3,9),(4,1,7),(4,4,5)]
新三元组表:
[ ( 1 , 2 , − 3 ) , ( 1 , 4 , 7 ) ⏟ 第一列对应的段,新三元组的下标是 0 到 1 , ( 2 , 1 , 12 ) ⏟ 第二列对应的段,下标是 2 , ( 3 , 3 , 9 ) ⏟ 第三列 . . . 下标是 3 , ( 4 , 2 , 4 ) , ( 4 , 4 , 5 ) ⏟ 第四列 . . . 下表是 4 到 5 ] [\underbrace{(1,2,-3),(1,4,7)}_{第一列对应的段,新三元组的下标是0到1},\underbrace{(2,1,12)}_{第二列对应的段,下标是2},\underbrace{(3,3,9)}_{第三列...下标是3},\underbrace{(4,2,4),(4,4,5)}_{第四列...下表是4到5}] [第一列对应的段,新三元组的下标是01 (1,2,3),(1,4,7),第二列对应的段,下标是2 (2,1,12),第三列...下标是3 (3,3,9),第四列...下表是45 (4,2,4),(4,4,5)]
代码如下:

Status FastTransposeSMatrix(TSMatrix M,TsMatrix *T){
	T->m=M.n;
	T->n=M.m;
	T->nonzeroNums=M.nonzeroNums;
	int col,t;
	int q=0;//新三元组表的下标
	int p=0;//旧三元组表的下标
	int num[M.n];//辅助数组,统计每一列的非零元个数
	int copt[M.n];//数组数组,统计每一列第一个元素在新三元组表中的位置(对应段的开始
	if(T->nonzeroNums){
		memset(num,0,sizeof(num));//num数组元素置0
		//遍历旧三元组表统计矩阵M每一列的非零元个数
		for(t=0;col<M.nonzeroNums;t++) num[M.data[t].j]++;
		copt[0]=0;
		//计算矩阵M每一列首个非零元在新三元组表中位置(段的起始位置,一列对应一个段)
		for(col=1;col<M.n;col++) copt[col]=copt[col-1]+num[col-1];
		//遍历旧三元组表
		for(p=0;p<M.nonzeroNums;p++){
			col=M.data[p].j;
			q=copt[col];//获取矩阵M的col的非零元在新三元表中的位置
			T->data[q].j=M.data[p].i;
			T->data[q].i=M.data[p].j;
			T->data[q].e=M.data[p].e;
			copt[col]++;
		}
	}
	return OK;
}

结论:相对比朴素思想的方法,快速转置时间复杂度更低,以空间换时间从而达到时间复杂度:O(n+nonzeroNums),即使nonzeroNums很大为m*n,时间复杂度最多也就和遍历矩阵的时间复杂度O(m x n)相当。
细节:快速转置里的copt的计算类似于计算数组的前缀和,每一列的存放位置都是基于上一列的末尾开始。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于JSP实习管理系统【附源码】

基于SSM的学生管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登录 5.1.2…

RuoYi-Vue若依 环境搭建 速成

一、若依简介 RuoYi-Vue 是一个开源的后台管理系统&#xff0c;适用于快速开发企业级应用。该平台由两部分组成&#xff1a;前端和后端。 &#xff08;1&#xff09;技术框架 前端技术&#xff1a; Vue.js: 前端框架使用 Vue.js&#xff0c;这是一种流行的JavaScript框架&a…

鸿蒙网络编程系列28-服务端证书锁定防范中间人攻击示例

1. TLS通讯中间人攻击及防范简介 TLS安全通讯的基础是基于对操作系统或者浏览器根证书的信任&#xff0c;如果CA证书签发机构被入侵&#xff0c;或者设备内置证书被篡改&#xff0c;都会导致TLS握手环节面临中间人攻击的风险。其实&#xff0c;这种风险被善意利用的情况还是很…

数据结构与算法——Java实现 44.翻转二叉树

目录 226. 翻转二叉树 思路 代码 本地代码测试 不管前方的路有多苦 只要走的方向正确 不管多么崎岖不平 都比站在原地更接近幸福 —— 24.10.21 226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输…

GEE引擎传奇UI界面修改教程

还记得小林之前给大家分享了gom引擎UI界面编辑教程&#xff0c;今天给大家分享一下gee引擎UI界面修改教程 首先打开登录器生成器-客户端界面设置 在客户端界面设置这里可以自定义UI素材&#xff0c;也可以直接在原素材上编辑主界面 传奇根目录指向的是你的传奇客户端根目录&am…

单神经元建模:基于电导的模型[神经元结构、静息电位和等效电路]

文章目录 神经元结构、静息电位和等效电路神经元结构静息电位能斯特方程1. **描述浓度比的非线性关系**&#xff1a;2. **化学势与电势的关系**&#xff1a;3. **对称性**&#xff1a;4. **热力学与平衡**&#xff1a;总结&#xff1a; GHK方程Nernst方程和GHK方程的对比 等效电…

深度学习:YOLO目标检测和YOLO-V1算法损失函数的计算

简介 YOLO&#xff08;You Only Look Once&#xff09;是一种基于深度学习的目标检测算法&#xff0c;它的核心思想是将目标检测问题转化为一个回归问题&#xff0c;通过一个神经网络直接预测目标的类别和位置。 YOLO算法将输入图像分成SxS个网格&#xff0c;每个网格负责预测…

cefsharp79.1.360(Chromium 79.0.3945.130)支持H264视频播放-PDF预览 老版本回顾系列体验

一、关于此版本 版本:Cef 79.1.36/CefSharp 79.1.360/Chromium 79.0.3945.130/支持H264/支持PDF预览 支持PDF预览和H264推荐版本 63/79/84/88/100/111/125 运行环境需要 visual c++ 2015不支持xp/vista/2003/2008默认不支持h264(版权问题)支持打印预览 print preview已知问题…

Kafka之原理解析

定义 Kafka 是一个分布式流媒体平台&#xff0c;kafka官网&#xff1a;http://kafka.apache.org/ Kafka 是一种高吞吐量、分布式、基于发布/订阅的消息系统&#xff0c;最初由 LinkedIn 公司开发&#xff0c;使用Scala 语言编写&#xff0c;目前是Apache 的开源项目。 流媒体…

深入解析Golang GMP

文章目录 1. 引言2. GMP 模型概述与核心结构体2.1. G&#xff08;Goroutine&#xff09;2.2. M&#xff08;Machine/Thread&#xff09;2.3. P&#xff08;Processor&#xff09;2.4. 全局调度器schedt&#xff08;Scheduler&#xff09; 3. Goroutine 的生命周期与状态管理3.1…

子比主题美化-用户中心隐私功能

前言 子比主题用户中心的文章、评论、粉丝等默认全部人可见&#xff0c;但是有时不想让全部人可见就可以开启此功能 图片展示 教程开始 把以下代码添加到子比主题下&#xff0c;按顺序找到该文件/inc/functions/zib-author.php&#xff0c;在zib-author.php第374行把原代码删…

面试官:`interrupted()` 和 `isInterrupted()` 你真的用懂了吗?

感谢Java面试教程的 Java面试题&#xff1a;interrupted和isInterrupted方法的区别 在Java中&#xff0c;interrupted() 和 isInterrupted() 是用于检查线程中断状态的方法&#xff0c;但它们之间有一些关键的区别。 方法类型&#xff1a; interrupted() 是一个静态方法&…

每月洞察:App Store 和 Google Play 的主要更新

Google Play 和 App Store 的算法不断发展&#xff0c;定期更新和变化会显着影响其功能。对于开发人员和营销人员来说&#xff0c;跟上这些变化至关重要&#xff0c;因为它们会直接影响应用发现和排名。 本文将深入探讨 Google Play 和 App Store 的最新更新&#xff0c;解释它…

基于微信小程序二手物品调剂系统设计与实现

文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图文章目录 前言 文章底部名片&#xff0c;获取项目的完整演示视频&#xff0c;免费解答技术疑问 项目介绍 二手物品调剂系统是一种在线平台&#xff0c;旨在促进用户之间的二手物品交易。该系统提供了一个…

【Pycharm】显示内存不足the IDE is running low on memory解决方法

Pycharm提示显示内存不足the IDE is running low on memory解决方法 在右上角找到Help&#xff0c;点击&#xff0c;找到change memory settings 修改数值如1024&#xff0c;2048 等&#xff0c;增大容量即可。最后点击save and Restart

Newstar_week1_week2_wp

week1 wp crypto 一眼秒了 n费马分解再rsa flag&#xff1a; import libnum import gmpy2 from Crypto.Util.number import * p 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297…

大数据之hive(分布式SQL计算工具)加安装部署

1.分布式SQL计算: 对数据进行统计分析&#xff0c; SQL是目前最为方便的编程工具. 2.hive:主要功能: 将 SQL语句翻译成MapReduce程序运行,提供用户分布式SQL计算能力 3.构建分布式SQL计算:(hive核心组件) 需要有: 一:元数据管理功能, 即&#xff1a;数据位置,数据结构,等对数…

每日OJ题_牛客_[NOIP2001]装箱问题_01背包_C++_Java

目录 牛客_[NOIP2001]装箱问题_01背包 题目解析 C代码 Java代码 牛客_[NOIP2001]装箱问题_01背包 [NOIP2001]装箱问题 (nowcoder.com) 描述&#xff1a; 有一个箱子容量为V&#xff08;正整数&#xff0c;0 ≤ V ≤ 20000&#xff09;&#xff0c;同时有n个物品&…

Vue3中ref和reactive的对比

1. ref 定义 用途: 用于创建基本数据类型或单一值的响应式引用。语法: const myRef ref(initialValue); 特性 返回一个包含 .value 属性的 Proxy 对象。适用于基本数据类型&#xff08;如数字、字符串、布尔值等&#xff09;和单一值。 import { ref } from vue;const co…

售后管理系统 解锁服务效率与质量双重提升

售后管理系统通过提升响应速度、确保服务一致性、数据分析优化流程&#xff0c;提高企业售后服务质量。ZohoDesk等解决方案可自动化分配工单、多渠道支持、管理追踪工单等&#xff0c;增强客户满意度和忠诚度。 一、什么是售后管理系统 首先&#xff0c;我们需要了解什么是售后…