本来准备昨天下午写的,但是因为去参加360众测靶场的考核耽搁了,靶场的题目还是挺基础的。

继续学习吧。

使用黑色墨水在白纸上签名就像由像素点构成的稀疏矩阵。如图4所示。

图4 手写体签名

【问题】请将以下稀疏点阵信息用三元组表进行存储,并:

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

(1)用稀疏矩阵快速转置法对该矩阵进行转置。转置前后的三元组表均以行序为主序。

(2) 以阵列形式输出转置前后的稀疏矩阵,如图5所示。

     

           图5  (a)转置前                   (b)转置后

先普及一下稀疏矩阵的概念:

简单理解稀疏矩阵就是元素大部分为零的矩阵,在实际生活中我们遇到的大型稀疏矩阵,如果按照常规的储存方法,就会造成大量空间的浪费,而且在访问和操作的时候也会造成大量时间上的浪费。三元组表就是为了解决这一问题而产生的解决方案之一。

稀疏矩阵由于其自身的稀疏特性,通过压缩可以大大节省稀疏矩阵的内存代价。具体操作是:将非零元素所在的行、列以及它的值构成一个三元组(i,j,v),然后再按某种规律存储这些三元组,这种方法可以节约存储空间,而这些三元组的集合,就是三元组表。

我们一步步来,将问题分解为一个个小模块,先将稀疏矩阵存储在三元组表中

因为C语言中没有三元组这种数据类型,所以我们先使用typedef定义三元组表:

typedef struct{
int i,j,val;
}NODE;

  i,j,val分别表示三元组表的行,列以及非零元素的值。

这里的需要储存的稀疏矩阵也一起定义了

int nums[11][10]={
{0,1,1,1,1,1,1,1,0,0},
{1,0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1}
};

  这里有一个问题是:

    将稀疏矩阵存储到三元组表中时,需要记录稀疏矩阵的行列值吗?

答案是肯定的,如果不存储稀疏矩阵的行列值,当遇到稀疏矩阵最后一行全部是0的情况,由稀疏矩阵得到的三元组表,是无法还原成原来的稀疏矩阵的。

在上面的稀疏矩阵中,一共有28个非零元素,行值为11,列值为10,所以我们需要申请29个三元组的储存空间,多余的那一个储存空间用来存储稀疏矩阵的行,列值,以及稀疏矩阵中非零元素的个数

#define T 28
NODE matrix[T+1];
matrix[0].i=11;
matrix[0].j=10;
matrix[0].val=0;

  用三元组表中第0个元素存储稀疏矩阵的基础信息

然后就到了将稀疏矩阵nums存储进三元组表的操作,遍历稀疏矩阵,当有元素为1的时候,将三元组表非零元素数量matrix[0].val++;然后依次将稀疏矩阵非零元素的信息存储进三元组表中

void init_matrix(NODE* matrix){
int i,j;
for(i=0;i<matrix[0].i;i++){
for(j=0;j<matrix[0].j;j++){
if(nums[i][j]==1){
matrix[0].val++;
matrix[matrix[0].val].i=i;
matrix[matrix[0].val].j=j;
matrix[matrix[0].val].val=nums[i][j];
}
}
}
}

  

存储了之后我们将三元组表输出还原成稀疏矩阵,看是否能正确还原

void put_matrix(NODE* matrix){
int i,j;
int count=1;
for(i=0;i<matrix[0].i;i++){
for(j=0;j<matrix[0].j;j++){
if(i==matrix[count].i&&j==matrix[count].j){
printf("%d",matrix[count].val);
count++;
}else{
printf("0");
}
}
printf("\n");
}
}

  程序运行的结果是

可以看到显示与原稀疏矩阵相同,说明存储入三元组表是正确的

接下来我们进行三元组表的快速转置,先贴一张上课时候的PPT

当然如果直接看PPT的话很有可能还是一头雾水,所以举一个形象的例子:

一个年级有四个班的同学,一班有20人,二班有30人,三班有40人,四班有50人,全年级的同学一起到电影院看电影,进了电影院之后,告诉我们要按照班级的顺序坐座位,座位只有一排,大家进电影院的时候都是跟自己熟悉的朋友一起,自然是乱序的,那我们应该怎么才能快速依次按照班级的顺序坐座位呢?

有一个办法是,大家随便坐座位,到了位置上之后,再进行班级顺序的比较,让一班的同学坐一班的位置,二班的同学坐二班的位置等等等,但是这样两两进行比较,效率未免过于低下。

另外的一个办法是,坐座位的时候,先把人群里面的一班同学筛选出来,依次放在一班同学的位置,然后再将人群里面的二班同学筛选出来,放在一班同学的后面位置,然后。。。

思考了之后,好像没有能够只筛选(遍历)一次人群就坐好座位的办法。

当然是!有的

所以就引入快速转置三元组表的办法,即如果我们提前知道每个班有多少人,在遍历人群的时候,只需要将其放在每个班开始的位置就行了。

如人群的开始班级排列是这样的 2,3,1,4,2,2,3.。。。。第一个同学是2班的,我们将其安排在第21号座位上(假设座位号是从1开始),因为我们知道前面会有20个一班的同学要坐座位,接着二号同学是三班的,安排在51号座位,三号同学是1班的,安排在1号座位,四号同学是四班的,安排在91号座位,五号同学!又是二班的,但是原来二班的同学已经坐了21号,所以我们理所当然地坐在22号座位,后面的也是这样,只需要遍历一次原人群,就能将同学们按照顺序坐在相应的位置上了。

但是在上面的描述中,我们需要知道每个班有多少人,以及每个班的第一个位置是在哪里(比如最开始二班同学的第一个位置是21号座位,有同学坐了21号座位之后,第一个位置自然就后移成22号座位了,等待下一个同学来坐,坐了之后再继续后移),所以需要两个辅助数组来存储这两个信息

num数组用来存储每个班中的人数,cpot数组用来存储每个班的第一个位置。

在三元组表快速转置中,num[i]表示原三元组表中第i列中非零元的个数,cpot[i]表示原三元组表中第i列中第一个非零元素的在新的三元组表中的位置(cpot这一段可能有点绕,再解释一下,因为在题目中,我们需要转置之后的三元组表按照行主序排列,由于是转置之后的,说明我们的顺序在转置之前,是按照列序在新的三元组表中放置,即如果新的三元组表中有(2,1),(1,2),那么(1,2)会在(2,1)的下面,因为2>1,找到其在三元组表中对应的位置后,再进行行列转置)

接下来将新三元组表中第0元素设置好

new_matrix[0].i=matrix[0].j;
new_matrix[0].j=matrix[0].i;
new_matrix[0].val=matrix[0].val;

  然后初始化num数组和cpot数组,这里需要提到的是两个PPT里面也显示了的关系,再贴一次:

cpot[i]表示原三元组表中第i列中第一个非零元素在新的三元组表中的位置,可以理解为同学们去看电影的时候,每个班在电影院座位上的最开始那个座位,比如二班的同学最开始是21,三班的同学是51,但是有一个显而易见的事实是(别告诉我你没看出来:-)当然没看出来也没事),一班的同学在座位中最开始的座位是1,这个是不需要遍历人群,也不需要由班级人数可以确定的,而其他每个班开始的位置,是上一个班的开始位置+上一个班的人数,如21=1+20,1是一班开始的座位,20是1班的人数,三班的同学由二班的位置又可以得到51=21+30,依次类推初始化cpot

for(i=0;i<matrix[0].j;i++){
num[i]=0;
}
for(i=1;i<=matrix[0].val;i++){
num[matrix[i].j]++;
}
cpot[0]=1;
for(i=1;i<matrix[0].j;i++){
cpot[i]=cpot[i-1]+num[i-1];
}

  最后就是最关键的转置的时候了,当然经过前面这么多的铺垫,最后这一步已经显得很简单了,和大家去看电影这个例子一样,按照步骤放位置就行了。

使用tmp变量存储该元素属于哪一列(哪一个班级),first变量存储其列在新的三元组表中存储的位置(该班级同学坐的第一个位置),然后将原三元组表中的数据转置之后放进去就行了,最后让这个列在新的三元组表中存储位置后移一位(后来的该班同学只好坐在这个同学后面啦),代码已经不是很重要了

for(i=1;i<=matrix[0].val;i++){
tmp=matrix[i].j;
first=cpot[tmp];
new_matrix[first].i=matrix[i].j;
new_matrix[first].j=matrix[i].i;
new_matrix[first].val=matrix[i].val;
cpot[tmp]++;
}

  分析完了之后是不是感觉也没那么难了呢,其实关键的代码只有那么几处,慢慢分析是可以缕清思路的。

转置后输出结果为:

完整代码如下:

#include<stdio.h>
#include<stdlib.h>
#define MAX 111 int nums[11][10]={
{0,1,1,1,1,1,1,1,0,0},
{1,0,0,0,0,0,0,0,1,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0},
{0,0,1,0,0,0,0,0,0,0},
{0,1,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1}
};
typedef struct{
int i,j,val;
}NODE;
#define T 28 void put_matrix(NODE* matrix){
int i,j;
int count=1;
for(i=0;i<matrix[0].i;i++){
for(j=0;j<matrix[0].j;j++){
if(i==matrix[count].i&&j==matrix[count].j){
printf("%d",matrix[count].val);
count++;
}else{
printf("0");
}
}
printf("\n");
}
}
void init_matrix(NODE* matrix){
int i,j;
for(i=0;i<matrix[0].i;i++){
for(j=0;j<matrix[0].j;j++){
if(nums[i][j]==1){
matrix[0].val++;
matrix[matrix[0].val].i=i;
matrix[matrix[0].val].j=j;
matrix[matrix[0].val].val=nums[i][j];
}
}
}
}
void put_nums(){
int i,j;
for(i=0;i<11;i++){
for(j=0;j<10;j++){
printf("%d",nums[i][j]);
}
printf("\n");
}
} void reverse_matrix(NODE* matrix,NODE* new_matrix){
int num[matrix[0].j],cpot[matrix[0].j];
int tmp,first,i;
new_matrix[0].i=matrix[0].j;
new_matrix[0].j=matrix[0].i;
new_matrix[0].val=matrix[0].val;
for(i=0;i<matrix[0].j;i++){
num[i]=0;
}
for(i=1;i<=matrix[0].val;i++){
num[matrix[i].j]++;
}
cpot[0]=1;
for(i=1;i<matrix[0].j;i++){
cpot[i]=cpot[i-1]+num[i-1];
}
//转置存储
for(i=1;i<=matrix[0].val;i++){
tmp=matrix[i].j;
first=cpot[tmp];
new_matrix[first].i=matrix[i].j;
new_matrix[first].j=matrix[i].i;
new_matrix[first].val=matrix[i].val;
cpot[tmp]++;
}
} int main(){
int i,j;
NODE matrix[T+1],new_matrix[T+1];
matrix[0].i=11;
matrix[0].j=10;
matrix[0].val=0;
printf("储存入三元组表前:\n");
put_nums();
printf("初始化三元组表...");
init_matrix(matrix);
printf("\n使用三元组表按行主序进行储存后:\n");
put_matrix(matrix);
printf("正在转置...");
reverse_matrix(matrix,new_matrix);
printf("\n转置后的结果为:\n");
put_matrix(new_matrix);
return 0;
}

  

以上

matrix[

稀疏矩阵三元组表快速转置(C语言实现)的更多相关文章

  1. 三元组表压缩存储稀疏矩阵实现稀疏矩阵的快速转置(Java语言描述)

    三元组表压缩存储稀疏矩阵实现稀疏矩阵的快速转置(Java语言描述) 用经典矩阵转置算法和普通的三元组矩阵转置在时间复杂度上都是不乐观的.快速转置算法在增加适当存储空间后实现快速转置具体原理见代码注释部 ...

  2. 稀疏矩阵三元组快速转置(转poklau123写的很清楚)

    关于稀疏矩阵的快速转置法,首先得明白其是通过对三元表进行转置.如果误以为是对矩阵进行转置,毫无疑问就算你想破脑袋也想不出个所以然,别陷入死胡同了! 对于一个三元表,行为i,列为j,值为v.需将其i与j ...

  3. sdut 1592转置矩阵【稀疏矩阵的压缩存储】【快速转置算法】

    转置矩阵 Time Limit: 1000ms   Memory limit: 32768K  有疑问?点这里^_^ 题目链接:http://acm.sdut.edu.cn/sdutoj/proble ...

  4. SDUT 3347 数据结构实验之数组三:快速转置

    数据结构实验之数组三:快速转置 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 转置运算是一 ...

  5. SDUT-3347_数据结构实验之数组三:快速转置

    数据结构实验之数组三:快速转置 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 转置运算是一种最简单的矩阵运算,对于一个 ...

  6. Java程序员快速入门Go语言

    这篇文章帮助Java程序员快速入门Go语言. 转载至 开源中国社区. http://www.oschina.net 本文将以一个有代表性的例子为开始,以此让Java程序员对Go语言有个初步认识,随后将 ...

  7. 现有 Vue.js 项目快速实现多语言切换的一种思路

    Web 项目多语言(i18n,即国际化)是比较常见的需求,常规的做法大概有以下几种: 每种语言单独开发页面,适用于 CMS 之类的网站 多语言文本和页面结构分离,运行时动态替换.适用于单页应用(SPA ...

  8. 记一些之前忘记积累的问题(fiddler 学习、XP系统不能上网、XP不能装fiddler、注册表快速找到)

    记一些之前忘记积累的问题: fiddler学习:http://www.cnblogs.com/kingwolf_JavaScript/archive/2012/11/07/FiddlerUI.html ...

  9. 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输

    002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...

随机推荐

  1. 如何用css设置鼠标属性的小手

    在元素中添加onmouseover="this.style.cursor='hand'"或者 cursor:pointer;

  2. python菜鸟教程学习3:基础语法

    菜鸟教程对应网址:https://www.runoob.com/python3/python3-basic-syntax.html 编码:python3用UTF-8编码,所有字符串都是unicode字 ...

  3. 主动关闭 time wait结构体

    /* * This is a TIME_WAIT sock. It works around the memory consumption * problems of sockets in such ...

  4. 位图bitmap应用

    所有比特的编号方法是,从低字节的低位比特位开始,第一个bit为0,最后一个bit为 n-1. 比如说,现在有个数组是这样子的,int a[4],那么a[0]的比特位为0--31a[1]的比特位为32- ...

  5. 使用GitHub API上传文件及GitHub做图床

    本文介绍GitHub API基础及上传文件到仓库API,并应用API将GitHub作为图床 GitHub API官方页面 GitHub API版本 当前版本为v3,官方推荐在请求头中显示添加版本标识. ...

  6. JS 实现飞机大战

    这是JS版本的飞机大战,和C#版本的思路相同,就是语言上有差别,用来巩固知识.可以将代码直接引入到HTML中就可以看到效果 //编写背景对象 function Background(width,hei ...

  7. MySQL存储索引InnoDB数据结构为什么使用B+树,而不是其他树呢?

    InnoDB的一棵B+树可以存放多少行数据? 答案:约2千万 为什么是这么多? 因为这是可以算出来的,要搞清楚这个问题,先从InnoDB索引数据结构.数据组织方式说起. 计算机在存储数据的时候,有最小 ...

  8. NPOT纹理与平铺模式OpenGL规范

    OpenGL规范从2.0开始支持显示边长为非2次幂的Texture,但限制条件是需要环绕模式为CLAMP_TO_EDGE并且过滤模式为NEAREST或者LINEAR. 解除限制的条件是硬件支持OES_ ...

  9. 使用Folx下载热门电影居然这么简单

    在闲暇的时候,很多人会选择观看电影.电视剧来打发时间.对于一些热门的资源,可以通过网页搜索的方式,找到很多与之相对应的种子资源. 但有时候,一些不那么热门的资源就要花费较多时间搜索.有了Folx bt ...

  10. 如何制作C语言基本数据类型的思维导图

    在使用C语言编写程序时,数据类型是一个非常重要的内容,任何一个不被重视的数据错误都会使编译器无法翻译,导致程序报错. 使用思维导图来梳理各个数据类型是一个很有效的记忆方法,接下来就为大家展示一下我用i ...