数据结构(超详细讲解!!)第二十一节 特殊矩阵的压缩存储

news2024/12/23 18:44:08

1.压缩存储的目标

值相同的元素只存储一次

压缩掉对零元的存储,只存储非零元

特殊形状矩阵:

是指非零元(如值相同的元素)或零元素分布具有一定规律性的矩阵。

如: 对称矩阵 上三角矩阵   下三角矩阵 对角矩阵   准对角矩阵

2.三角矩阵

三角矩阵大体分为三类:下三角矩阵、上三角矩阵和对称矩阵。

对于一个n阶矩阵A来说,若当i<j时,有aij=0,则称此矩阵为下三角矩阵;

若当i>j时,有aij=0,则称此矩阵为上三角矩阵;

若矩阵中的所有元素均满足aij=aji,则称此矩阵为对称矩阵。

 对于下三角矩阵的压缩存储,我们只存储下三角的非零元素,对于零元素则不存。我们按“行序为主序”进行存储,得到的序列是a11, a21, a22, a31, a32, a33, …, an1, an2, …, ann。由于下三角矩阵的元素个数为n(n+1)/2,即:

所以可压缩存储到一个大小为n(n+1)/2的一维数组C中

下三角矩阵中元素aij(i>j)在一维数组A中的位置为:        

Loc[i, j]=Loc[1, 1]+前i-1行非零元素个数+第i行中aij前非零元素个数        

前i-1行元素个数=1+2+3+4+…+(i-1)=i(i-1)/2,所以有 Loc[i, j]=Loc[1, 1]+i(i-1)/2+j-1        

同样,对于上三角矩阵,也可以将其压缩存储到一个大小为n(n+1)/2的一维数组C中。其中元素aij(i<j)在数组C中的存储位置为: Loc[i, j]=Loc[1, 1]+j(j-1)/2+i-1      

对于对称矩阵,因其元素满足aij=aji,我们可以为每一对相等的元素分配一个存储空间,即只存下三角(或上三角)矩阵,从而将n2个元素压缩到n(n+1)/2个空间中。

3.带状矩阵

 三对角带状矩阵有如下特点:            

i=1, j=1, 2                  

1<i<n, j=i-1, i, i+1;            

i=n, j=n-1, n;

时,aij非零,其它元素均为零。

(1)确定存储该矩阵所需的一维向量空间的大小        

在这里我们假设每个非零元素所占空间的大小为1个单元。 从图中观察得知,三对角带状矩阵中,除了第一行和最后一行只有2个非零元素外,其余各行均有3个非零元素。由此得到, 所需一维向量空间的大小为 2+2+3(n-2)=3n-2

(2)确定非零元素在一维数组空间中的位置        

Loc[i ,  j] = Loc[1, 1]+前i-1行非零元素个数+第i行中aij前非零元素个数;        

前i-1行元素个数=3×(i-1)-1(因为第1行只有2个非零元素);        

第i行中aij前非零元素个数=j-i+1,其中

由此得到:

Loc[i,  j]=Loc[1, 1]+3(i-1)-1+j-i+1 =Loc[1, 1]+2(i-1)+j-1

4.稀疏矩阵

是指非零元比零元少得多,且非零元在矩阵中的分布不具有一定规律性的矩阵。

假设 m 行 n 列的矩阵含 t 个非零元素,则称

 

为稀疏因子。 通常认为 小于等于0.05 的矩阵为稀疏矩阵。

(1)稀疏矩阵的三元组表表示法

对于矩阵中的每个非零元,可以用三个属性来惟一确定:它所在的行、所在的例以及它的值。因此,可以用一个三元组(行, 列, 值)来惟一确定矩阵中的一个非零元。

   稀疏矩阵的三元组表表示法虽然节约了存储空间, 但比起矩阵正常的存储方式来讲,其实现相同操作要耗费较多的时间, 同时也增加了算法的难度, 即以耗费更多时间为代价来换取空间的节省。

#define MAXSIZE 1000   /*非零元素的个数最多为1000*/ 
      typedef struct 
      {
         int    row,   col;    /*该非零元素的行下标和列下标*/
         ElementType  e;   /*该非零元素的值*/ 
      }Triple;  
      typedef struct 
 {
      Triple   data[MAXSIZE+1];     /* 非零元素的三元组表,data[0]未用*/
      int      m,   n,   len;           /*矩阵的行数、 列数和非零元素的个数*/ 
}TSMatrix; 

 1) 用三元组表实现稀疏矩阵的转置运算        

下面首先以稀疏矩阵的转置运算为例,介绍采用三元组表时的实现方法。        

所谓的矩阵转置,是指变换元素的位置,把位于(row,col)位置上的元素换到(col,row)位置上,也就是说, 把元素的行列互换。

采用矩阵的正常存储方式时, 实现矩阵转置的经典算法如下:

void  TransMatrix(ElementType source[n][m],  ElementType dest[m][n])
{/*source和dest分别为被转置的矩阵和转置以后的矩阵(用二维数组表示)*/ 
     int i,  j;  
     for(i=0; i<m; i++)
        for (j=0; j< n; j++) 
            dest[i][ j]=source[j] [i] ; 
  }

采用矩阵的三元组存储方式实现转置

① 矩阵source的三元组表A的行、 列互换就可以得到B中的元素

 ② 为了保证转置后的矩阵的三元组表B也是以“行序为主序”进行存放,则需要对行、列互换后的三元组表B按B的行下标(即A的列下标)大小重新排序

方法一:

 我们附设一个位置计数器j,用于指向当前转置后元素应放入三元组表B中的位置。 处理完一个元素后,j加1, j的初值为1。 具体转置算法如下:

Void  TransposeTSMatrix(TSMatrix A,  TSMatrix  *B)
{ /*把矩阵A转置到B所指向的矩阵中去, 矩阵用三元组表表示 */
      int  i ,  j,  k ;  
      B->m= A.n ;  B->n= A.m ;  B->len= A.len ; 
      if(B->len>0)
      { 
j=1;  
            for(k=1;  k<=A.n;  k++)   
              for(i=1;  i<=A.len;  i++)  
                  if(A.data[i].col==k)   
                  {   
                        B->data[j].row=A.data[i].col    
                        B->data[j].col=A.data[i].row;     
                        B->data[j].e=A.data[i].e;     
                        j++;    
                  }
      }
} 

  算法的时间耗费主要是在双重循环中,其时间复杂度为O(A.n×A.len), 最坏情况下,当A.len=A.m×A.n时,时间复杂度为O(A.m×A.n2)。采用正常方式实现矩阵转置的算法时间复杂度为O(A.m×A.n)。

方法二:

 为了能将待转置三元组表A中元素一次定位到三元组表B的正确位置上,需要预先计算以下数据:

  (1) 待转置矩阵source每一列中非零元素的个数(即转置后矩阵dest每一行中非零元素的个数)。

(2) 待转置矩阵source每一列中第一个非零元素在三元组表B中的正确位置(即转置后矩阵dest每一行中第一个非零元素在三元组B中的正确位置)。

 为此, 需要设两个数组num[ ]和position[ ],其中num[col]用来存放三元组表A中第col列中非零元素个数(三元组表B中第col行非零元素的个数),position[col]用来存放转置前三元组表A中第col列(转置后三元组表B中第col行)中第一个非零元素在三元组表B中的正确位置。

num[col]的计算方法: 将三元组表A扫描一遍,对于其中列号为k的元素,给相应的num[k]加1。

position[col]的计算方法: position[1]=1, position[col]=position[col-1]+num[col-1],  其中2≤col≤A.n。

  将三元组表A中所有的非零元素直接放到三元组表B中正确位置上的方法:        

position[col]的初值为三元组表A中第col列(三元组表B的第col行)中第一个非零元素的正确位置,当三元组表A中第col列有一个元素加入到三元组表B时,则position[col]=position[col]+1,即: 使position[col]始终指向三元组表A中第col列中下一个非零元素的正确位置。        

具体算法如下:

FastTransposeTSMatrix (TSMatrix  A,   TSMatrix * B)
{ /*基于矩阵的三元组表示, 采用快速转置法, 将矩阵A转置为B所指的矩阵*/
int col,  t,  p, q; 
int num[MAXSIZE],  position[MAXSIZE]; 
B->len=A.len;  B->n=A.m;  B->m=A.n; 
if(B->len)
   {
for(col=1; col<=A.n; col++) 
            num[col]=0;   
      for(t=1; t<=A.len; t++) 
            num[A.data[t].col]++;   /*计算每一列的非零元素的个数*/
      position[1]=1; 
      for(col=2; col<A.n; col++)   /*求col列中第一个非零元素在B.data[ ]中的正
确位置 */
      position[col]=position[col-1]+num[col-1];  
      for(p=1; p<A.len.p++) 
       {  
            col=A.data[p].col;   q=position[col];  
            B->data[q].row=A.data[p].col;   
            B->data[q].col=A.data[p].row;   
            B->data[q].e=A.data[p].e
            position[col]++;  
       } 
}
} 

 快速转置算法的时间主要耗费在四个并列的单循环上,这四个并列的单循环分别执行了A.n,A.len,A.n-1,A.len次,因而总的时间复杂度为O(A.n)+O(A.len)+O(A.n)+O(A.len),即为O(A.n+A.len)。 当待转置矩阵M中非零元素个数接近于A.m×A.n 时,其时间复杂度接近于经典算法的时间复杂度O(A.m×A.n)。          

快速转置算法在空间耗费上除了三元组表所占用的空间外,还需要两个辅助向量空间,即num[1..A.n],position[1..A.n]。可见,算法在时间上的节省,是以更多的存储空间为代价的。

(2)稀疏矩阵的链式存储结构: 十字链表

与用二维数组存储稀疏矩阵比较,用三元组表表示的稀疏矩阵节约了空间,但是在进行矩阵加法、减法和乘法等运算时,有时矩阵中的非零元素的位置和个数会发生很大的变化。如A=A+B, 将矩阵B加到矩阵A上,此时若还用三元组表表示法,势必会为了保持三元组表“以行序为主序”而大量移动元素。

在十字链表中,矩阵的每一个非零元素用一个结点表示, 该结点除了(row,col,value)以外,还要有以下两个链域:       

 right:  用于链接同一行中的下一个非零元素;        

down: 用于链接同一列中的下一个非零元素。

用两个一维的指针数组分别存放各行链表的头指针和各列链表的头指针,从而得到了矩阵的十字链表存储结构。

结构类型:

建十字链表的算法的时间复杂度为O(t×s),s=max(m,n)。

typedef struct OLNode
 {
      int                row,   col;           /* 非零元素的行和列下标 */ 
      ElementType     value;  
      struct OLNode   * right, *down;   /* 非零元素所在行表、列表的后继链域 */
 }OLNode;  *OLink; 
      typedef struct 
 { 
      OLink  * row-head,   *col-head;    /* 行、 列链表的头指针向量 */ 
      int     m,   n,   len;                   /* 稀疏矩阵的行数、 列数、 非
零元素的个数 */
 }CrossList; 


CreateCrossList (CrossList * M)
 {/* 采用十字链表存储结构, 创建稀疏矩阵M */
 if(M!=NULL) free(M); 
 scanf(&m, &n, &t);    /* 输入M的行数, 列数和非零元素的个数 */
 M->m=m; M->n=n; M->len=t; 
 If(!(M->row-head=(OLink * )malloc((m+1)sizeof(OLink)))) exit(OVERFLOW); 
 If(!(M->col-head=(OLink * )malloc((n+1)sizeof(OLink)))) exit(OVERFLOW); 
 M->row-head[ ]=M->col-head[ ]=NULL;
    /* 初始化行、 列头指针向量, 各行、 列链表为空的链表 */
 for(scanf(&i, &j, &e); i!=0;  scanf(&i, &j, &e)) 
     { 
     if(!(p=(OLNode *) malloc(sizeof(OLNode)))) exit(OVERFLOW);  
     p->row=i; p->col=j; p->value=e;    /* 生成结点 */ 
     if(M->row-head[i]==NULL)   M->row-head[i]=p; 
else
      {  /* 寻找行表中的插入位置 */
      for(q=M->row-head[i];   q->right&&q->right->col<j;   q=q->right)
        p->right=q->right;  q->right=p;    /* 完成插入 */ 
      } 
     if(M->col-head[j]==NULL)   M->col-head[j]=p;  
    else
      {  /*寻找列表中的插入位置*/
      for(q=M->col-head[j];   q->down&&q->down->row<i;   q=q->down) 
           p->down=q->down;  q->down=p;     /* 完成插入 */         
       }
    }
 } 

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

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

相关文章

汽车标定技术(二)--基于XCP的标定测量实战

目录 1.工程创建 1.1 新建工程 1.2 设备配置 1.3 标定观测 1.4 刷写 2.原始hex文件与标定文件的合并 2.1 修改memory segment file 2.2 标定量地址偏移 ​编辑 2.3 标定后与原始hex文件合并 2.4 标定后直接merge 2.5 不用对ram地址进行偏移实现hex文件合并 本文使用…

算法?认识一下啦

一、什么是算法&#xff1f; 算法 &#xff0c;是对特定问题求解方法和步骤的一种描述。它是有限指令的有限序列&#xff0c;其中每个指令表示一个或多个操作。 算法和程序的关系 算法​是解决问题的一种方法或一个过程&#xff0c;考虑如何将输入转换成输出&#xff0c;一个…

小程序如何设置用户同意服务协议并上传头像和昵称

为了保护用户权益和提供更好的用户体验&#xff0c;设置一些必填项和必读协议是非常必要的。首先&#xff0c;用户必须阅读服务协议。服务协议是明确规定用户和商家之间权益和义务的文件。通过要求用户在下单前必须同意协议&#xff0c;可以确保用户在使用服务之前了解并同意相…

Android Studio布局

线性布局 水平或竖直排列子元素的布局容器 相对布局 可针对容器内每个子元素设置相对位置&#xff08;相对于父容器或同级子元素的位置&#xff09; 网格布局 找了下面这篇文章连接可以参考&#xff08;不再赘述&#xff09; GridLayout(网格布局) | 菜鸟教程 (runoob.com) …

【原创】java+swing+mysql校园共享单车管理系统设计与实现

摘要&#xff1a; 校园共享单车作为一种绿色、便捷的出行方式&#xff0c;在校园内得到了广泛的应用。然而&#xff0c;随着单车数量的增加&#xff0c;管理难度也不断加大。如何提高单车的利用率和管理效率&#xff0c;成为校园共享单车发展面临的重要问题。本文针对这一问题…

Python武器库开发-常用模块之collections模块(十七)

常用模块之collections模块(十七) 除python提供的内置数据类型&#xff08;int、float、str、list、tuple、dict&#xff09;外&#xff0c;collections模块还提供了其他数据类型: 计数器&#xff08;counter&#xff09;有序字典&#xff08;orderedDict&#xff09;可命名元…

基于动力学模型的机械臂滑膜控制

一、滑模控制设计思路 参考资料&#xff1a;https://zhuanlan.zhihu.com/p/463230163&#xff08;思路理解&#xff09; https://blog.csdn.net/xiaohejiaoyiya/article/details/90271529&#xff08;干扰的处理&#xff09; 滑模控制的思路有两个关键&#xff0c;一个是设计…

【C++】多态 ⑫ ( 多继承 “ 弊端 “ | 多继承被禁用的场景 | 菱形继承结构的二义性 | 使用虚继承解决菱形继承结构的二义性 )

文章目录 一、多继承 " 弊端 "1、多继承被禁用的场景2、多继承弊端 二、代码示例 - 多继承弊端1、错误示例 - 菱形继承结构的二义性2、代码示例 - 使用虚继承解决菱形继承结构的二义性 一、多继承 " 弊端 " 1、多继承被禁用的场景 禁止使用多继承的场景 : …

LV.12 D16 轮询与中断 学习笔记

一、CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需要一段时间后再次询问&#xff0c;周而复始 中断 CPU执行程序时若硬件需要其服务&#xff0c;对应的硬件给CPU发送中断信号&#xff0c…

如何使用Python和Matplotlib创建双Y轴动态风格折线图 | 数据可视化教程

前言 我的科研论文中需要绘制一个精美的折线图&#xff0c;我的折线图中有三条曲线&#xff0c;分别表示期望角速度指令信号&#xff0c;和实际的角速度信号&#xff0c;还有实际的航向角信号&#xff0c;现在我已经拥有了数据&#xff0c;使用Python中matplotlib.plt.plot来直…

Java之图书管理系统

&#x1f937;‍♀️&#x1f937;‍♀️&#x1f937;‍♀️ 今天给大家分享一下Java实现一个简易的图书管理系统&#xff01; 清风的个人主页&#x1f389;✏️✏️ &#x1f302;c/java领域新星创作者 &#x1f389;欢迎&#x1f44d;点赞✍评论❤️收藏 &#x1f61b;&…

SpringBoot-SpringCache缓存

文章目录 Spring Cache 介绍常用注解 Spring Cache 介绍 Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache 提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;…

基于级联延迟信号消除的锁相环(CDSC_PLL)技术MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 基于级联型延迟信号消除&#xff08;CDSC&#xff09;的锁相环技术&#xff08;CDSC-PLL&#xff09;&#xff0c;该锁相环克服了传统dq 锁相环在电网电压畸变或不对称时存在较大稳态误差的缺点。CDSC-PLL是在…

windows 用vs创建cmake工程并编译opencv应用项目生成exe流程简述

目录 前言一、安装opencv&#xff08;1&#xff09;下载&#xff08;2&#xff09;双击安装&#xff08;3&#xff09;环境变量和system文件夹设置 二、打开vs创建项目三、编辑cpp&#xff0c;.h&#xff0c;cmakelist.txt文件&#xff08;1&#xff09;h文件&#xff08;2&…

Python-敲木鱼升级版(真手动版敲木鱼)

演示效果 需要安装的第三方库&#xff1a; pip install pygame # 加载音乐 pip install pillow # 加载图片 pip install mediapipe # 判断手势的模型 pip install opencv # 模型要用来处理图形 建议有独显和摄像头的可以尝试&#xff01; 想着升级一下玩法&#xff0c;只有真敲…

二维码智慧门牌管理系统升级:引领政务服务、寄件、开锁、刻章新潮流

文章目录 前言一、政务服务二、寄件服务三、便民开锁和刻章服务四、应用范围 前言 在科技不断进步的时代&#xff0c;二维码智慧门牌管理系统升级版正在改变我们的生活&#xff0c;为政务服务、寄件、便民开锁、刻章等多种业务应用提供全新的解决方案&#xff0c;使我们的日常…

python基础(Python高级特性(切片、列表生成式)、字符串的正则表达式、函数、模块、Python常用内置函数、错误处理)培训讲义

文章目录 1. Python高级特性&#xff08;切片、列表生成式&#xff09;a) 切片的概念、列表/元组/字符串的切片切片的概念列表切片基本索引简单切片超出有效索引范围缺省 扩展切片step为正数step为负数 b) 列表生成式以及使用列表生成式需要注意的地方概念举例说明1. 生成一个列…

详细讲解如何求解「内向基环森林」问题

题目描述 这是 LeetCode 上的 「2876. 有向图访问计数」 &#xff0c;难度为 「困难」。 Tag : 「基环森林」、「内向基环树」、「拓扑排序」、「图」、「BFS」 现有一个有向图&#xff0c;其中包含 n 个节点&#xff0c;节点编号从 0 到 n - 1。此外&#xff0c;该图还包含了 …

OJ练习第185题——数组中两个数的最大异或值

数组中两个数的最大异或值 力扣链接&#xff1a;421. 数组中两个数的最大异或值 题目描述 给你一个整数数组 nums &#xff0c;返回 nums[i] XOR nums[j] 的最大运算结果&#xff0c;其中 0 ≤ i ≤ j < n 。 示例 官解思路 异或运算性质&#xff1a; class Solutio…

ZZ038 物联网应用与服务赛题第I套

2023年全国职业院校技能大赛 中职组 物联网应用与服务 任 务 书 &#xff08;I卷&#xff09; 赛位号&#xff1a;______________ 竞赛须知 一、注意事项 1.检查硬件设备、电脑设备是否正常。检查竞赛所需的各项设备、软件和竞赛材料等; 2.竞赛任务中所使用的各类软件工…