稀疏矩阵的三元组表表示法及其转置

news2024/10/11 8:27:53

1. 什么是稀疏矩阵

稀疏矩阵是指矩阵中大多数元素为零的矩阵。

从直观上讲,当元素个数低于总元素的30%时,这样的矩阵被称为稀疏矩阵。

由于该种矩阵的特点,我们在存储这种矩阵时,如果直接采用二维数组,就会十分浪费空间,因为其中大多数元素都是重复的零。

稀疏矩阵的三元组表表示法

对于稀疏矩阵的压缩存储,采用只存储非零元素的方法。

由于稀疏矩阵中非零元素a_{ij}的分布没有规律,因此在存储非零元素值得同时,还必须存储该非零元素在矩阵中的位置信息,即行号和列号。

也就是采用三元组的结构存储:

为处理方便,将稀疏矩阵中非零元素对应的三元组行号依次增大进行存放。

这也就解释了,为什么稀疏矩阵是非零元素占比小于30%的矩阵。

因为采取三元组的结构储存,一个元素会占用三个单元的空间,只有当零元素占比小于30%时,这种存储结构才能在空间上有较明显的收益。

稀疏矩阵三元组表的类型定义

#define ElementType int

//一个三元组元素
typedef struct 
{
    int row, col;//非零元素行下标和列下标
    ElementType e;
}Triple;

//稀疏矩阵
typedef struct 
{
    Triple* data;//非零元素的三元组表
    int m, n, len;//矩阵行数,列数,非零元素个数
    int capacity;//容量
}TSMatrix;

 2. 对稀疏矩阵进行基本操作

//初始化稀疏矩阵
void TSMInite(TSMatrix* ps)
{
    assert(ps);
    ps->data = NULL;
    ps->m = TSM_ROWMAX;
    ps->n = TSM_COLMAX;
    ps->len = 0;
    ps->capacity = 0;
}

//销毁稀疏矩阵
void TSMDestroy(TSMatrix* ps)
{
    assert(ps);
    free(ps->data);
    ps->data = NULL;
    ps->len = 0;
    ps->capacity = 0;
}

//检查扩容
void CheckCapacity(TSMatrix* ps)
{
    assert(ps);
    if(ps->capacity == ps->len)
    {
        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        Triple* tmp = (Triple*)realloc(ps->data, newcapacity * sizeof(Triple));
        if(tmp == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->data = tmp;
        ps->capacity = newcapacity;
    }
}

//插入元素
void ElemInsert(TSMatrix* ps, Triple x)
{
    assert(ps);
    CheckCapacity(ps);
    if(ps->len == 0)
    {
        ps->data[ps->len] = x;
    }
    if(x.row < ps->data[0].row)
    {
        for(int j = ps->len; j > 0; j--)
        {
            ps->data[j] = ps->data[j - 1];
        }
        ps->data[0] = x;
    }
    else
    for(int i = 0; i < ps->len; i++)
    {
        if(x.row >= ps->data[i].row&&x.row <= ps->data[i + 1].row||i == ps->len - 1)
        {
            for(int j = ps->len; j > i + 1; j--)
            {
                ps->data[j] = ps->data[j - 1];
            }
            ps->data[i + 1] = x;
            break;
        }
    }
    ps->len++;
}

//打印元素
void TSMPrint(TSMatrix ps)
{
    for(int i = 0; i < ps.len; i++)
    {
        printf("row = %d col = %d e = %d\n", ps.data[i].row, ps.data[i].col, ps.data[i].e);
    }
    printf("\n");
}

这些函数基本是以顺序表操作函数为蓝本写的,目的是为了方便我们实现稀疏矩阵的转置。

只不过插入元素的函数需要确保插入之后,三元组的行号是依次递增的。

3. 稀疏矩阵的转置

需要强调的是,矩阵的常规存储是二维的,而三元组表存储是一维的,由于矩阵存储结构的变化,也带来了运算方法的不同,必须认真分析。

3.1 稀疏矩阵转置的经典算法

void TransMatrix(ElementType source[m][n], ElementType dest[n][m])
{
    int i, j;
    for(i = 0; i < m; i++)
    for(j = 0; j < n; j++)
    dest[j][i] = source[i][j];
}

这个算法是针对传统的二维数组的存储方式。

3.2 用三元组表实现稀疏矩阵的转置

假设A和B是稀疏矩阵source和dest的三元组表,则实现转置的简单方法如下:

1. 三元组表A的行,列互换就可以得到B中的元素。

2. 转置后的矩阵的三元组表B中的三元组不是以“行序为主序”存储的,为保证三元组表B也是以“行序为主序”进行存放的,则需要对该三元组表B按行下标(即A的列下标)以递增顺序重新排列。

 上图中的步骤很容易实现,但是重新排序势必会大量移动元素,从而影响算法的效率。

为避免上述简单转置算法中重新排序引起的元素移动,可采取接下来的两种处理方法。

3.2.1 列序递增转置法

算法思想

这里的列序指的是A的列,也就是按照A的列序来将元素转置到B中。

即将A的第一列全部转置到B中(得到B的第一行)后,再将A的第二列全部转置到B中,以此类推。

代码

//列序递增转置法
void TSMSwitch1(TSMatrix A, TSMatrix* B)
{
    assert(B);
    TSMDestroy(B);
    B->data = (Triple*)malloc(A.capacity * sizeof(Triple));
    B->capacity = A.capacity;
    B->len = A.len;
    B->m = A.n;
    B->n = A.m;
    int j = 0;//记录B当前空位
    for(int k = 0; k < A.n; k++)
    {
        for(int i = 0; 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的第一列全部转置之后才能转置第二列,每列转置需要重新扫描一次A。

那么,我们有没有办法使得各列同时进行存放呢,这样就只用扫描一次A了。

3.2.2 一次定位快速转置法

算法思想

如果我们知道A的每一列有多少个元素,那么就可以推知B中每一行的起始位置。

这样一来,假如某次在A中扫描到第n列的元素,我们就可以直接将其放到B中的第n行所在位置,而不用先放完一列再放下一列。

所以,我们准备进行三次循环:

1. 定义数组num,数组的下标表示A的列,遍历A并将每一列元素的个数记录在num中。

2. 定义数组position,数组下标表示B的行,遍历position,根据A每一列元素的个数得到对应行的起始位置。

3. 遍历A,根据position数组,将A中的元素转置到B的对应行。

代码 

//一次定位快速转置算法
void TSMSwitch2(TSMatrix A, TSMatrix* B)
{
    assert(B);
    TSMDestroy(B);
    B->data = (Triple*)malloc(A.capacity * sizeof(Triple));
    B->capacity = A.capacity;
    B->len = A.len;
    B->m = A.n;
    B->n = A.m;
    int num[B->m];
    int position[B->m];
    memset(num, 0, B->m * sizeof(int));
    memset(num, 0, B->m * sizeof(int));
    for(int i = 0; i < A.len; i++)
    {
        num[A.data[i].col]++;
    }
    position[0] = 0;
    for(int row = 1; row < B->m; row++)
    {
        position[row] = position[row - 1] + num[row - 1];
    }
    for(int i = 0; i < A.len; i++)
    {
        B->data[position[A.data[i].col]].row = A.data[i].col;
        B->data[position[A.data[i].col]].col = A.data[i].row;
        B->data[position[A.data[i].col]].e = A.data[i].e;
        position[A.data[i].col]++;
    }
}

算法分析

显然,一次定位快速转置算法的时间效率要高得多,它在时间性能上由于列序递增转置法,但在空间耗费上增加了两个辅助向量空间,即num和position。

由此可见,算法在时间上的节省是以更多的储存空间为代价的。

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

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

相关文章

环形链表问题(判环+寻找入环点)

文章目录 题目1.判断链表中是否有环1.1 思路分析&#xff08;快慢指针&#xff09;1.2 思考&#xff1a;为什么快指针每次走两步&#xff0c;慢指针每次走一步两者一定可以相遇&#xff1f;1.3 快指针一次走3步&#xff0c;走4步&#xff0c;...n步行吗&#xff1f; 题目2. 寻找…

一、企业级架构之LNMP

一、LNMP 概述 1、LNMP之间的关系&#xff1a; LNMP Linux Nginx MySQL PHP 2、配置LNMP服务器&#xff1a; (1) 克隆一台centos7虚拟机&#xff0c;修改 IP 地址 和 UUID 编号。 IP 为 10.1.1.10&#xff0c;UUID 修改后三位。 (2) 设置主机名称&#xff0c;绑定IP地…

机器学习周记(第三十二周:文献阅读-时空双通路框架)2024.3.25~2024.3.31

目录 摘要 ABSTRACT 1 论文信息 1.1 论文标题 1.2 论文摘要 1.3 论文模型 1.3.1 Spatial Encoder&#xff08;空间编码器&#xff09; 1.3.2 Temporal Encoder&#xff08;时间编码器&#xff09; 2 相关代码 摘要 本周阅读了一篇运用GNN进行时间序列预测的论文。论文…

挖一挖:PostgreSQL Java里的double类型存储到varchar精度丢失问题

前言 大概故事是这样的&#xff0c;PostgreSQL数据库&#xff0c;表结构&#xff1a; create table t1(a varchar);然后使用标准的Java jdbc去插入数据&#xff0c;其基本代码如下&#xff1a; import java.sql.*; public class PgDoubleTest {public static void main(Stri…

渐进式图片解决前端在页面中使用大图,图片体积过大导致页面出现白屏现象

1、演示 可以看到&#xff0c;图片还在拼命加载的时候&#xff0c; 页面上就已经有内容了 2、什么渐进式图片 图片一开始是模糊的&#xff0c;然后逐渐的开始变的清晰。如果页面上有一些大图&#xff0c;如果直接扔给浏览器的话那么图片的传输时间就会比较长&#xff0c;用户就…

Java对象Object对象头-MarkWord分析-hashCode

代码主要通过打印对象的内存布局来观察对象头在不同状态下的变化&#xff0c;进而分析对象头在不同情况下的内存布局情况。 System.out.println(ClassLayout.parseInstance(o).toPrintable());&#xff1a;这一行代码通过使用开源库 openjdk.jol 的 ClassLayout 类来解析对象 o…

算法思想堪比哲学,你知多少否?

对算法思想 - 分治算法的理解 分治算法是一种将复杂问题划分为规模较小的子问题&#xff0c;并递归地解决这些子问题&#xff0c;最后将它们的解合并为原问题的解的算法思想。 它具有以下几个关键步骤&#xff1a;分解、解决和合并。 通过将大问题分解为小问题&#xff0c;每个…

数据结构—堆

什么是堆 堆是一种特殊的树形结构&#xff0c;其中每个节点都有一个值。堆可以分为两种类型&#xff1a;最大堆和最小堆。在最大堆中&#xff0c;每个节点的值都大于等于其子节点的值&#xff1b;而在最小堆中&#xff0c;每个节点的值都小于等于其子节点的值。这种特性使得堆…

Linux实验过程

答案截图获取&#xff0c;代写&#xff1a; https://laowangall.oss-cn-beijing.aliyuncs.com/studentall.pdf 基本任务&#xff1a; 1.Linux操作系统安装 2.vi文本编辑 3. Linux用户及文件管理命令 4. Linux权限管理命令 5. Linux网络服务 提高任务&#xff1a; 1、Li…

vue3+elementPlus:实现数字滚动效果(用于大屏可视化)

自行封装注册一个公共组件 案例一&#xff1a; //成功案例&#xff1a; //NumberScroll.vue /* 数字滚动特效组件 NumberScroll */<template><span class"number-scroll-grow"><spanref"numberScroll":data-time"time"class&qu…

intellij idea 使用git的 cherry pick 摘取其他分支的comment

cherry pick 摘取其他分支的comment 如果想把 feature_v1.0 分支的comment 摘到 feature_v1.0_new 分支上&#xff0c; 先切换到 feature_v1.0_new分支&#xff0c;这一步不能少了。然后点击 下面菜单栏的 git&#xff0c;点击Local Changes旁边的 Log&#xff0c;这时能看到…

【Java】打包:JAR、EAR、WAR

打包&#xff1a;JAR、EAR、WAR war 是一个 Web 模块&#xff0c;其中需要包括 WEB-INF&#xff0c;是可以直接运行的 WEB 模块。而 jar 一般只是包括一些 class 文件&#xff0c;在声明了 main_class 之后是可以用 java 命令运行的。 它们都是压缩的包&#xff0c;拿 Tomcat …

SpringBoot登录校验(四)过滤器Filter

JWT令牌生成后&#xff0c;客户端发的请求头中会带有JWT令牌&#xff0c;服务端需要校验每个请求的令牌&#xff0c;如果在每个controller方法中添加校验模块&#xff0c;则十分复杂且冗余&#xff0c;所以引入统一拦截模块&#xff0c;将请求拦截下来并做校验&#xff0c;这块…

配置Pod使用PersistentVolume作为存储,PV类型为 hostPath

准备开始 在节点主机上创建一个 /mnt/data 目录&#xff1a; mkdir -p /mnt/data创建一个index.html文件 echo Hello from Kubernetes storage > /mnt/data/index.html创建PV 创建一个 hostPath 类型的 PersistentVolume。 Kubernetes 支持用于在单节点集群上开发和测试的…

第19次修改了可删除可持久保存的前端html备忘录:换了一个特别的倒计时时钟

第19次修改了可删除可持久保存的前端html备忘录:换了一个特别的倒计时时钟 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><met…

数据结构记录

之前记录的数据结构笔记&#xff0c;不过图片显示不了了 数据结构与算法(C版) 1、绪论 1.1、数据结构的研究内容 一般应用步骤&#xff1a;分析问题&#xff0c;提取操作对象&#xff0c;分析操作对象之间的关系&#xff0c;建立数学模型。 1.2、基本概念和术语 数据&…

glm2大语言模型服务环境搭建

一、模型介绍 ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本&#xff0c;在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上&#xff0c;ChatGLM2-6B 引入了如下新特性&#xff1a; 更强大的性能&#xff1a;基于 ChatGLM 初代模型的开发经验&…

大数据实验三-HBase编程实践

目录 一&#xff0e;实验内容 二&#xff0e;实验目的 三&#xff0e;实验过程截图及说明 1、安装HBase 2、配置伪分布式模式&#xff1a; 3、使用hbase的shell命令来操作表&#xff1a; 4、使用hbase提供的javaAPI来编程实现类似操作&#xff1a; 5、实验总结及心得体会…

『VUE』10. 事件修饰符(详细图文注释)

目录 什么是事件修饰符?vuejs 不使用修饰符 原生js实现禁用事件对象的默认事件使用事件修饰符 .prevent使用事件修饰符 .stop使用事件修饰符 .self 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 什么是事件修饰符? vue 在 Vu…

『51单片机』蜂鸣器

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…