pytorch统计矩阵非0的个数_矩阵的三种存储方式---三元组法 行逻辑链接法 十字链表法...

在介绍矩阵的压缩存储前,我们需要明确一个概念:对于特殊矩阵,比如对称矩阵,稀疏矩阵,上(下)三角矩阵,在数据结构中相同的数据元素只存储一个。 @[TOC]

三元组顺序表

稀疏矩阵由于其自身的稀疏特性,通过压缩可以大大节省稀疏矩阵的内存代价。具体操作是:将非零元素所在的行、列以及它的值构成一个三元组(i,j,v),然后再按某种规律存储这些三元组,这种方法可以节约存储空间 。 如下图所示为一个稀疏矩阵,我们应该怎么样存储呢?

43aa4a20c97c33e2587a8e8526f2b032.png 若对其进行压缩存储,我们可以将一个非零数组元素的三元看成一个单位存入一维数组,具体如下所示。比如(1,1,1)代表第一行第一列的元素值为1。注意,这里我们只存储非零值。

cbfc47a3f3a2b13c7eae16fe63254260.png 具体代码如下:
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUMBER  3//三元组结构体
typedef struct {//行标r,列标cint r,c;//元素值int data;
}triple;
//矩阵的结构表示
typedef struct {//存储该矩阵中所有非0元素的三元组triple data[NUMBER];//row和column分别记录矩阵的行数和列数,num记录矩阵中所有的非0元素的个数int row,column,num;
}TSMatrix;
//输出存储的稀疏矩阵
void print(TSMatrix M);
int main() {int i;srand((unsigned)time(NULL));TSMatrix M;M.row=3;M.column=3;M.num=3;//初始化矩阵for(i=0;i<M.num;i++){//随机数范围[1,3]M.data[i].r=rand()%M.num+1;M.data[i].c=rand()%M.num+1;M.data[i].data=rand()%10;}print(M);return 0;
}
void print(TSMatrix M){for(int i=1;i<=M.row;i++){for(int j=1;j<=M.column;j++){int value =0;for(int k=0;k<M.num;k++){//遍历时的r,c行列值和实际存储row,column行列值的比较,相同的话就说明有非零元素,打印存储的非0值if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].data);value =1;break;}}if(value == 0)printf("0 ");}printf("n");}
}

行逻辑链接的顺序表

使用三元组顺序表存储稀疏矩阵,我们每次访问其中一个元素都要遍历整个矩阵,效率比较低。我们可以使用一个一维数组来存储每行第一个非零元素在一维数组中的位置,这样就可以提升访问效率。这样的表就叫做行逻辑链接的顺序表。 下图为一个稀疏矩阵,当使用行逻辑链接的顺序表对其进行压缩存储时,需要做以下两个工作:

ec4fe049a50877f2aeea0a706fac71f2.png 1.将矩阵中的非 0 元素采用三元组的形式存储到一维数组 data 中:

9055167d578567d34720e40c8a7d0fd9.png 2.使用数组 rpos 记录矩阵中每行第一个非 0 元素在一维数组中的存储位置。

5dd72c512defaebb1f5795f7b3daddd9.png 通过以上两步操作,即实现了使用行逻辑链接的顺序表存储稀疏矩阵。 此时,如果想从行逻辑链接的顺序表中提取元素,则可以借助 rpos 数组提高遍历数组的效率。 例如,提取图 1 稀疏矩阵中的元素 2 的过程如下: 由 rpos 数组可知,第一行首个非 0 元素位于data[1],因此在遍历此行时,可以直接从第 data[1] 的位置开始,一直遍历到下一行首个非 0 元素所在的位置(data[3])之前; 同样遍历第二行时,由 rpos 数组可知,此行首个非 0 元素位于 data[3],因此可以直接从第 data[3] 开始,一直遍历到下一行首个非 0 元素所在的位置(data[4])之前;遍历第三行时,由 rpos 数组可知,此行首个非 0 元素位于 data[4],由于这是矩阵的最后一行,因此一直遍历到 rpos 数组结束即可(也就是 data[tu],tu 指的是矩阵非 0 元素的总个数)。 具体代码如下
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXSIZE 12500
#define MAXRC 100
typedef struct
{//行,列int r,c;//元素值int e;
}Triple;typedef struct
{//矩阵中元素的个数Triple  data[MAXSIZE];//每行第一个非零元素在data数组中的位置int rpos[MAXRC];//行数,列数,元素个数int row,column,number;
}RLSMatrix;
//矩阵的输出函数
void print(RLSMatrix M){for(int i=1;i<=M.row;i++){for(int j=1;j<=M.column;j++){int value=0;if(i+1 <=M.row){for(int k=M.rpos[i];k<M.rpos[i+1];k++){if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].e);value=1;break;}}if(value==0){printf("0 ");}}else{for(int k=M.rpos[i];k<=M.number;k++){if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].e);value=1;break;}}if(value==0){printf("0 ");}}}printf("n");}
}
int main()
{int i;//srand((unsigned)time(NULL));RLSMatrix M;//矩阵的大小M.number = 4;M.row = 3;M.column = 4;//每一行首个非零元素在一维数组中的位置M.rpos[1] = 1;M.rpos[2] = 3;M.rpos[3] = 4;M.data[1].e = 3;M.data[1].r = 1;M.data[1].c = 2;M.data[2].e = 5;M.data[2].r = 1;M.data[2].c = 4;M.data[3].e = 1;M.data[3].r = 2;M.data[3].c = 3;M.data[4].e = 2;M.data[4].r = 3;M.data[4].c = 1;//输出矩阵print(M);return 0;
}

十字链表法

关于十字链表法,具体可看下图。我们把矩阵的每一行每一列分别看成一个链表,然后将每一行和每一列的链表的第一个元素存放在一个数组中。这个数组就叫行链表的头指针数组,列链表的头指针数组。当我们访问矩阵的时候,就可以从行/列头指针数组中取出对应的指针,就可以访问这一行或者这一列的元素了。

bcf428e7764ddba6969bbbfb84ce49c5.png

7f40aae9a1cc954d45fcc80f2f572887.png

链表中节点的结构应如下图。所以,除了定义三元组的行,列,数值外,我们还需要定义指向行的指针,指向列的指针。最后还需要定义一个存放行/列链表头结点的数组专门存放各行各列的头结点。具体代码如下。

99d05b883bdba8a8fe7e519793fcccb6.png
typedef struct CLNode
{//矩阵三元组i代表行 j代表列 e代表当前位置的数据int r, c, data; //指针域 行指针 列指针struct CLNode *prow, *pcolumn; 
}CLNode, *CLink;
typedef struct
{//行和列链表头数组  CLink rhead[] 这样写也可以。写成指针是为了方便动态分配内存CLink *rhead, *chead; //矩阵的行数,列数和非零元的个数int rows, columns, num;  
}CrossList;

下面我们将要根据用户输入的行数,列数,非零元素的值,来创建矩阵。

//注意检查用户的输入do {  flag = 1;      printf("输入矩阵的行数、列数和非0元素个数:");scanf("%d%d%d",&m,&n,&t);if (m<0 || n<0 || t<0 || t>m*n)  flag = 0;  }while (!flag);  M.rows = m;M.columns = n;M.num = t;

用户输入合法的情况下我们要创建并初始化存放行列链表头的数组。

    //因为下标从1开始,所以头结点指针多分配一个内存if (!(M.rhead = (CLink*)malloc((m + 1) * sizeof(CLink))) || !(M.chead = (CLink*)malloc((n + 1) * sizeof(CLink)))){printf("初始化矩阵失败rn");exit(0);}// 初始化行头指针向量;各行链表为空链表 for (r = 1; r <= m; r++){M.rhead[r] = NULL;}// 初始化列头指针向量;各列链表为空链表    for (c = 1; c <= n; c++){M.chead[c] = NULL;}

存放行列链表头的数组准备好了,接下来我们就要创建数据节点了。根据用户输入的行号,列好,数值创建节点。这里同样要检查用户的输入。

for (scanf("%d%d%d", &r,&c,&data); ((r<=0)||(c<=0)); scanf("%d%d%d", &r,&c,&data))
{if (!(p = (CLNode*)malloc(sizeof(CLNode)))){printf("初始化三元组失败");exit(0);}p->r = r;p->c = c;p->data= data;
}

当创建好一个节点之后,我们就要放到行或者列的正确的位置。根据输入的行号列号确定插入的位置。那么应该怎样去插入?分两种情况 1、当这一行中没有结点的时候,那么我们直接插入 2、当这一行中有结点的时候我们插入到正确的位置 对于第一个问题,因为之前已经对头结点数组进行了初始化NULL,所以直接根据NULL==M->rhead[i]就可以判断一行中有没有节点。 对于第二个问题,当行中有节点的时候,无非是插入到某个节点之前或者某个节点之后。什么时候插入到节点前?什么时候插入到节点后呢? 1.插入节点前:当我们要插入的节点的列号小于已经存在的节点的列号,这个时候就要插入到这个节点之前了。 2.插入节点后:当我们要插入的节点的列号大于已经存在的节点的列号,这个时候就要插入到这个节点之后了。 对于第一种情况,代码如下。

//p为准备插入的节点,要插入到M.rhead[r]之前。if (NULL == M.rhead[r] || M.rhead[r]->c> c){p->prow = M.rhead[r];//M.rhead[r]要始终指向行的第一个节点M.rhead[r] = p;}

对于第二种情况,我们要插入的节点插入到已有节点的后面,那么,已有将要插入节点的列号必定大于已有节点的列号。我们只要找到一个节点比我们将要插入节点的列号大的节点就好,然后插入到这个节点的前面。如果现有的结点没有一个结点列号是大于要插入的节点的列号的,那么我们就应该插入到最后一个结点之后!

              //我们要找到一个比q节点大的节点。在这个节点之前插入for (q = M.rhead[r]; (q->prow) && q->prow->c < c; q = q->prow);p->prow = q->prow;q->prow = p;

对于列的插入同样如此,就不一一分析了,下面给出具体代码。

       //链接到列的指定位置if (NULL == M.chead[c] || M.chead[c]->r> r){p->pcolumn = M.chead[c];M.chead[c] = p;}else{for (q = M.chead[c]; (q->pcolumn) && q->pcolumn->r < r; q = q->pcolumn);p->pcolumn = q->pcolumn;q->pcolumn = p;}     

打印矩阵 对于十字链表矩阵的打印,我们每次从行/列头结点数组中取出每一行或者每一列的第一个节点依次往下访问就可以了,和普通的链表访问没有区别。如果对链表不熟悉的可以参考这篇文章史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)

void PrintClist(CrossList M)
{for (int i = 1; i <= M.num; i++){if (NULL != M.chead[i]){CLink p = M.chead[i];while (NULL != p){printf("%dt%dt%dn",p->r, p->c, p->data);p = p->pcolumn;}}}   
}

完整代码如下:

/** @Description: 十字链表存储压缩矩阵* @Version: V1.0* @Autor: Carlos* @Date: 2020-05-26 16:43:48* @LastEditors: Carlos* @LastEditTime: 2020-05-28 14:40:19*/ 
#include<stdio.h>
#include<stdlib.h>
typedef struct CLNode
{//矩阵三元组i代表行 j代表列 e代表当前位置的数据int r, c, data; //指针域 行指针 列指针struct CLNode *prow, *pcolumn; 
}CLNode, *CLink;
typedef struct
{//行和列链表头指针CLink *rhead, *chead; //矩阵的行数,列数和非零元的个数int rows, columns, num;  
}CrossList;
CrossList InitClist(CrossList M)
{CLNode *p,*q;int r,c,data;int m, n, t;int flag;//  printf("输入矩阵的行数、列数和非0元素个数:");// scanf("%d%d%d",&m,&n,&t);do {  flag = 1;      printf("输入矩阵的行数、列数和非0元素个数:");scanf("%d%d%d",&m,&n,&t);if (m<0 || n<0 || t<0 )  flag = 0;  }while (!flag);  M.rows = m;M.columns = n;M.num = t;//因为下标从1开始,所以头结点指针多分配一个内存if (!(M.rhead = (CLink*)malloc((m + 1) * sizeof(CLink))) || !(M.chead = (CLink*)malloc((n + 1) * sizeof(CLink)))){printf("初始化矩阵失败rn");exit(0);}// 初始化行头指针向量;各行链表为空链表 for (r = 1; r <= m; r++){M.rhead[r] = NULL;}// 初始化列头指针向量;各列链表为空链表    for (c = 1; c <= n; c++){M.chead[c] = NULL;}
//行数列数不为0
for (scanf("%d%d%d", &r,&c,&data); ((r<=0)||(c<=0)); scanf("%d%d%d", &r,&c,&data))
{if (!(p = (CLNode*)malloc(sizeof(CLNode)))){printf("初始化三元组失败");exit(0);}p->r = r;p->c = c;p->data= data;//链接到行的指定位置。 if (NULL == M.rhead[r] || M.rhead[r]->c> c){p->prow = M.rhead[r];M.rhead[r] = p;}else{for (q = M.rhead[r]; (q->prow) && q->prow->c < c; q = q->prow);p->prow = q->prow;q->prow = p;} //链接到列的指定位置if (NULL == M.chead[c] || M.chead[c]->r> r){p->pcolumn = M.chead[c];M.chead[c] = p;}else{for (q = M.chead[c]; (q->pcolumn) && q->pcolumn->r < r; q = q->pcolumn);p->pcolumn = q->pcolumn;q->pcolumn = p;}     }return M;
}void PrintClist(CrossList M)
{for (int i = 1; i <= M.num; i++){if (NULL != M.chead[i]){CLink p = M.chead[i];while (NULL != p){printf("%dt%dt%dn",p->r, p->c, p->data);p = p->pcolumn;}}}   
}
int main()
{CrossList M;M.rhead = NULL;M.chead = NULL;M = InitClist(M);PrintClist(M);return 0;
}

文中代码均已测试,有任何意见或者建议均可联系我。欢迎学习交流! 如果觉得写的不错,请点个赞再走,谢谢! 如遇到排版错乱的问题或者有任何疑问、建议,可以在“我的主页”找到我的联系方式和我的博客链接。

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

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

相关文章

【视频】2017,50个令人屏息的科技瞬间

来源&#xff1a;甲子光年概要&#xff1a;整个世界的巨变&#xff0c;肇始于一颗微尘的颤动。「甲子光年」挑选了属于2017年的50个科技瞬间。十年后再回望时&#xff0c;它们可能正是那个撼动世界的历史性时刻。即将过去的2017年&#xff0c;一定有这样的一瞬&#xff1a;整个…

机加工程序工时程序_准终工时、人工工时、机器工时,十个工程师九个会弄错...

​上一篇文章种蚂蚁先生跟大家详细分析了产品成本的组成&#xff0c;其主要分为材料成本和制造成本两个部分。 然而要得到制造成本&#xff0c;则必须有标准工时数据。那么标准工时究竟是什么呢&#xff1f;​标准工时制&#xff1a; 标准工时是在标准工作环境下&#xff0c;进…

互联网大脑的云反射弧路径选择问题,兼谈ET大脑模糊认知反演理论

互联网大脑的云反射弧路径选择问题研究2008年以来&#xff0c;我们在互联网大脑架构和互联网进化的研究中提到&#xff0c;“互联网大脑架构&#xff0c;就是互联网向与人类大脑高度相似的方向进化过程中&#xff0c;形成的类脑巨系统架构。互联网云脑架构具备不断成熟的类脑视…

如何传入比较器_typescript专题(五) 装饰器

欢迎来到我专题文章【typescript】&#xff0c;更多干货内容持续分享中&#xff0c;敬请关注&#xff01;本章目标基于webpack4.x从0开始搭建ts的开发环境ts中的装饰器的基本使用基于webpack4.x从0开始搭建ts的开发环境webpack4.x已经问世好久了&#xff0c;0配置是一大亮点&am…

『报告』IDC:2018年物联网产业10大预测

来源&#xff1a;T客汇编译概要&#xff1a;根据市场研究公司IDC的报告&#xff0c;2018年全球物联网支出总额将达到7720亿美元。新年新气象&#xff0c;2017年被称作物联网&#xff08;IoT&#xff09;元年&#xff0c;而2018年还将续写IoT的高歌猛进。根据市场研究公司IDC的报…

hbase获取表信息_HBase的读写和javaAPI的使用

一、hbase系统管理表hbase:namespace&#xff0c;记录了hbase中所有namespace的信息 ,当前系统下有哪些namespace信息scan hbase:namespacehbase:meta&#xff0c;记录了region信息scan hbase:meta二、读写思想client(get、scan)rowkey条件(1)由于rowkey是存储在region上的(2)判…

机器学习必知的15大框架

作者 | Devendra Desale译者 | Mags来源 | 云栖社区不管你是一个研究人员&#xff0c;还是开发者&#xff0c;亦或是管理者&#xff0c;想要使用机器学习&#xff0c;需要使用正确的工具来实现。本文介绍了当前最流行15个机器学习框架。机器学习工程师是开发产品和构建算法团队…

区分大小屏幕_第一个Python程序——在屏幕上输出文本

本节我将给大家介绍最简单、最常用的 Python 程序——在屏幕上输出一段文本&#xff0c;包括字符串和数字。Python 使用 print 函数在屏幕上输出一段文本&#xff0c;输出结束后会自动换行。在屏幕上输出字符串字符串就是多个字符的集合&#xff0c;由双引号" "或者单…

2018 年物联网发展五大趋势预测

来源&#xff1a;腾股创投作者 &#xff1a;Pramod Chandrayan物联网已经开始在所有行业的企业中走向主流。 到 2018 年底&#xff0c;物联网支出预计将增长 15&#xff05;&#xff0c;达到 7725 亿美元&#xff0c;毫无疑问&#xff0c;未来一年连接设备和企业物联网项目数量…

js中立即执行函数会预编译吗_JavaScript预编译过程

什么是预编译&#xff1f;当js代码执行时有三个步骤&#xff1a;1.语法分析&#xff0c;这个过程检查出基本的语法错误。2&#xff0c;预编译&#xff0c;为对象分配空间。3&#xff0c;解释执行&#xff0c;解释一行执行一行&#xff0c;一旦出错立即停止执行。预编译发生在代…

知识图谱火了,但你知道它的发展历史吗?|赠书5本

作者&#xff1a;尼克 编辑&#xff5c;Emily版式由AI前线整理知识图谱火了&#xff0c;但你知道它的发展历史吗&#xff1f;本文节选自《人工智能简史》第 3 章&#xff0c;从第一个专家系统 DENDRAL 到语义网再到谷歌的开源知识图谱&#xff0c;对知识图谱的发展历程进行了…

英特尔爆发史诗级芯片漏洞,Linux之父痛斥英特尔公司

来源&#xff1a;CEO来信概要&#xff1a;1月4日消息&#xff0c;英特尔处理器存在芯片级漏洞的消息仍在发酵&#xff0c;恐怕将会成为计算机行业发展史上最大的安全漏洞之一。1月4日消息&#xff0c;英特尔处理器存在芯片级漏洞的消息仍在发酵&#xff0c;恐怕将会成为计算机行…

曲线均匀分布_曲线篇:深刻理解B 样条曲线(下)

前两篇中讲解了贝塞尔曲线和B样条基础。FrancisZhao&#xff1a;曲线篇: 贝塞尔曲线​zhuanlan.zhihu.comFrancisZhao&#xff1a;曲线篇&#xff1a;深刻理解B 样条曲线&#xff08;上&#xff09;​zhuanlan.zhihu.com本文讲一下B样条的进阶clamped B样条由于我们常用的B样条…

JSON.parse()解析单引号错误的问题

1、当用JSON.parse()方法&#xff0c;解析从后台传来的json字符串&#xff0c;而且字符串中含有单引号(如Iam ....)&#xff0c;页面将出现如下异常 2、页面引起报错部分 3、解决办法&#xff1a;在后台将特殊字符转义 bookBlockVoListJSONString BaseStringUtils.getJsonFor…

Facebook人工智能实验室提出「全景分割」,实现实例分割和语义分割的统一

原文来源&#xff1a;arxiv作者&#xff1a;Alexander Kirillov、Kaiming He1、Ross Girshick、Carsten Rother、Piotr Dollar「雷克世界」编译&#xff1a;嗯~阿童木呀、KABUDA现如今&#xff0c;我们提出并研究了一种新的“全景分割”&#xff08;Panoramic segmentation&…

art.dialog弹出框后再根据其内部iframe的大小调整大小

1、主页面&#xff0c;用来弹出弹出页面 //查看错误小练习$.exerciseClick function(rowId ){$.event.get().preventDefault();var tdScore $("#" rowId).find("[aria-describedbyhomeworkStudentListGrid_score]");if( parseInt(tdScore.html()) 100 …

句子录音打分代码参考

1、页面名称:sentenceReadRecordDialogAnyone.jsp 2、页面效果 3、页面源码 <% page language"java" pageEncoding"UTF-8" contentType"text/html;charsetUTF-8"%> <% include file"/common/taglibs.jsp"%><script la…

工具用途_见微知著,小工具大用途!

为什么同一个饮品&#xff0c;店家做的尝起来如此美味。拿到配方后自己做的不是甜了就是酸了呢&#xff1f;相信很多朋友都有这种疑惑。但真相往往就会在不经意间被忽视&#xff0c;很多初学者并没有老师傅那样的熟练手感&#xff0c;可以做到信手拈来的程度。当没有那些量勺&a…

带暂停功能的音频播放代码参考

1、页面来源&#xff1a;sentenceExercise07.jsp 2、页面效果 3、源代码 <% page language"java" pageEncoding"UTF-8" contentType"text/html;charsetUTF-8"%> <% include file"/common/taglibs.jsp"%> <% include f…

Yann LeCun 怒喷 Sophia:这就是彻头彻尾的骗局

来源&#xff1a;雷锋网&#xff08;leiphone-sz&#xff09;作者&#xff1a;杨晓凡在各种机器学习方法火热、神经网络加速芯片和生成式模型火热、研究人员们常常为优秀论文拍案叫绝的 2017 年中&#xff0c;普通大众视野中的热门人工智能成果除了 AlphaGo&#xff0c;还有一件…