【数据结构】数组和字符串(五):特殊矩阵的压缩存储:稀疏矩阵——压缩稀疏行(CSR)
文章目录
- 4.2.1 矩阵的数组表示
- 4.2.2 特殊矩阵的压缩存储
- a. 对角矩阵的压缩存储
- b~c. 三角、对称矩阵的压缩存储
- d. 稀疏矩阵的压缩存储——三元组表
- e. 压缩稀疏行(Compressed Sparse Row,CSR)矩阵
- 结构体
- 创建CSR矩阵
- 元素设置
- 初始化
- 打印矩阵
- 销毁CSR矩阵
- 主函数
- 代码整合
4.2.1 矩阵的数组表示
【数据结构】数组和字符串(一):矩阵的数组表示
4.2.2 特殊矩阵的压缩存储
矩阵是以按行优先次序将所有矩阵元素存放在一个一维数组中。但是对于特殊矩阵,如对称矩阵、三角矩阵、对角矩阵和稀疏矩阵等, 如果用这种方式存储,会出现大量存储空间存放重复信息或零元素的情况,这样会造成很大的空间浪费。为节约存储空间和算法(程序)运行时间,通常会采用压缩存储的方法。
- 对角矩阵:指除了主对角线以外的元素都为零的矩阵,即对 任意 i ≠ j (1≤ i , j ≤n),都有M(i, j)=0。由于只有主对角线上有非零元素,只需存储主对角线上的元素即可。
- 三角矩阵:指上三角或下三角的元素都为零的矩阵。同样地,只需存储其中一部分非零元素,可以节省存储空间。
- 对称矩阵:指矩阵中的元素关于主对角线对称的矩阵。由于对称矩阵的非零元素有一定的规律,可以只存储其中一部分元素,从而减少存储空间。
- 稀疏矩阵:指大部分元素为零的矩阵。传统的按行优先次序存储方法会浪费大量空间来存储零元素,因此采用压缩存储的方法更为合适。常见的压缩存储方法有:压缩稠密行(CSR)、压缩稠密列(CSC)、坐标列表(COO)等。
a. 对角矩阵的压缩存储
【数据结构】数组和字符串(二):特殊矩阵的压缩存储:对角矩阵——一维数组
b~c. 三角、对称矩阵的压缩存储
【数据结构】数组和字符串(三):特殊矩阵的压缩存储:三角矩阵、对称矩阵——一维数组
d. 稀疏矩阵的压缩存储——三元组表
【数据结构】数组和字符串(四):特殊矩阵的压缩存储:稀疏矩阵——三元组表
e. 压缩稀疏行(Compressed Sparse Row,CSR)矩阵
压缩稀疏行(Compressed Sparse Row,CSR)是一种常用的稀疏矩阵存储格式。CSR存储格式通过压缩非零元素的行指针和列索引,以及存储非零元素的值,来有效地表示稀疏矩阵。它包含以下几个关键组成部分:
- row_ptr(行指针数组):它是一个长度为rows + 1的数组,用于存储每一行在col_indices和elements数组中的起始索引位置。row_ptr[i]表示第i行的元素在col_indices和elements数组中的起始位置,而row_ptr[i+1] - row_ptr[i]表示第i行的非零元素个数。
- col_indices(列索引数组):它是一个长度为num_elements的数组,用于存储每个非零元素对应的列索引。col_indices[i]表示第i个非零元素所在的列索引。
- elements(元素数组):它是一个长度为num_elements的数组,用于存储每个非零元素的值。elements[i]表示第i个非零元素的值。
CSR存储格式的主要优点是有效地压缩了稀疏矩阵的存储空间,只存储非零元素及其对应的行和列信息。此外,CSR格式还支持高效的稀疏矩阵向量乘法和稀疏矩阵乘法等操作。
结构体
typedef struct {int row;int col;int value;
} Element;typedef struct {int rows;int cols;int num_elements;Element* elements;int* row_ptr;int* col_indices;
} CSRMatrix;
-
Element结构体表示矩阵中的一个元素,包含三个成员变量:row(行索引)、col(列索引)和value(元素值)。 -
CSRMatrix结构体表示一个CSR矩阵,包含了矩阵的行数rows、列数cols、非零元素的个数num_elements,以及三个指针成员变量elements、row_ptr和col_indices。
创建CSR矩阵
CSRMatrix createCSRMatrix(int rows, int cols, int num_elements) {CSRMatrix matrix;matrix.rows = rows;matrix.cols = cols;matrix.num_elements = num_elements;matrix.elements = (Element*)malloc(num_elements * sizeof(Element));matrix.row_ptr = (int*)malloc((rows + 1) * sizeof(int));matrix.col_indices = (int*)malloc(num_elements * sizeof(int));return matrix;
}
createCSRMatrix函数用于创建一个CSR矩阵。- 接受矩阵的行数、列数和非零元素的个数作为参数,并返回创建的CSR矩阵。
- 在函数内部,通过动态内存分配分别为
elements、row_ptr和col_indices分配内存空间,并将row_ptr数组的所有元素初始化为0,最后返回创建的矩阵。
元素设置
void setElement(CSRMatrix* matrix, int row, int col, int value) {if (row < 0 || row >= matrix->rows) {printf("Invalid row index.\n");return;}int index = matrix->row_ptr[row];matrix->elements[index].row = row;matrix->elements[index].col = col;matrix->elements[index].value = value;matrix->col_indices[index] = col;matrix->row_ptr[row]++; // 递增索引值
}
setElement 函数可用于设置(修改)CSR矩阵中某个位置的元素值。
- 接受一个指向CSR矩阵的指针
matrix,以及要设置的元素的行索引、列索引和值作为参数。 - 在函数内部,首先检查行索引是否有效,如果无效则打印错误信息并返回。
- 然后,根据行索引找到对应行的起始位置,将元素的行索引、列索引和值分别赋给对应的矩阵元素,并更新
col_indices数组和row_ptr数组中的值。
初始化
void initializeCSRMatrix(CSRMatrix* matrix, int* values, int* row_indices, int* col_indices, int num_elements) {for (int i = 0; i < num_elements; i++) {matrix->elements[i].value = values[i];matrix->elements[i].row = row_indices[i];matrix->elements[i].col = col_indices[i];matrix->col_indices[i] = col_indices[i];matrix->row_ptr[row_indices[i]]++;}int sum = 0;for (int i = 0; i <= matrix->rows; i++) {int temp = matrix->row_ptr[i];matrix->row_ptr[i] = sum;sum += temp;}
}
initializeCSRMatrix 函数用于初始化CSR矩阵的数据。
- 接受一个指向CSR矩阵的指针
matrix,以及包含非零元素的值、行索引和列索引的数组,以及非零元素的个数作为参数。 - 通过遍历非零元素数组,将值、行索引和列索引分别赋给对应的矩阵元素,并更新
col_indices数组和row_ptr数组中的值。row_ptr数组的每个元素表示对应行的非零元素在elements数组中的起始位置,通过累加非零元素的个数来计算每行的结束位置。
打印矩阵
void printCSRMatrix(CSRMatrix matrix) {printf("CSR Matrix:\n");printf("Rows: %d, Cols: %d, Num Elements: %d\n", matrix.rows, matrix.cols, matrix.num_elements);printf("Values: ");for (int i = 0; i < matrix.num_elements; i++) {printf("%d ", matrix.elements[i].value);}printf("\n");printf("Row Pointer: ");for (int i = 0; i <= matrix.rows; i++) {printf("%d ", matrix.row_ptr[i]);}printf("\n");printf("Column Indices: ");for (int i = 0; i < matrix.num_elements; i++) {printf("%d ", matrix.col_indices[i]);}printf("\n");
}
void printMatrixForm(CSRMatrix matrix) {printf("Matrix Form:\n");for (int i = 0; i < matrix.rows; i++) {for (int j = 0; j < matrix.cols; j++) {int value = 0;for (int k = matrix.row_ptr[i]; k < matrix.row_ptr[i + 1]; k++) {if (matrix.elements[k].col == j) {value = matrix.elements[k].value;break;}}printf("%d ", value);}printf("\n");}
}
printCSRMatrix函数用于打印CSR矩阵的信息:它接受一个CSR矩阵作为参数,并打印矩阵的行数、列数、非零元素的个数以及elements、row_ptr和col_indices数组的内容。printMatrixForm函数用于以矩阵形式打印CSR矩阵。它接受一个CSR矩阵作为参数,并按矩阵的行数和列数遍历矩阵元素,通过遍历row_ptr数组和col_indices数组来获取每个位置的元素值,并打印出矩阵的形式。
销毁CSR矩阵
void destroyCSRMatrix(CSRMatrix* matrix) {free(matrix->elements);free(matrix->row_ptr);free(matrix->col_indices);matrix->elements = NULL;matrix->row_ptr = NULL;matrix->col_indices = NULL;
}
主函数
int main() {int rows = 9;int cols = 3;int num_elements = 5;CSRMatrix matrix = createCSRMatrix(rows, cols, num_elements);int col_indices[] = {0, 0, 0, 0, 1};int row_indices[] = {3, 5, 7, 8, 7};int values[] = {2, 1, 3, 1, 4};initializeCSRMatrix(&matrix, values, row_indices, col_indices, num_elements);printCSRMatrix(matrix);printMatrixForm(matrix);destroyCSRMatrix(&matrix);return 0;
}

代码整合
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct {int row;int col;int value;
} Element;typedef struct {int rows;int cols;int num_elements;Element* elements;int* row_ptr;int* col_indices;
} CSRMatrix;CSRMatrix createCSRMatrix(int rows, int cols, int num_elements) {CSRMatrix matrix;matrix.rows = rows;matrix.cols = cols;matrix.num_elements = num_elements;matrix.elements = (Element*)malloc(num_elements * sizeof(Element));matrix.row_ptr = (int*)malloc((rows + 1) * sizeof(int));matrix.col_indices = (int*)malloc(num_elements * sizeof(int));memset(matrix.row_ptr, 0, (rows + 1) * sizeof(int));return matrix;
}
void initializeCSRMatrix(CSRMatrix* matrix, int* values, int* row_indices, int* col_indices, int num_elements) {for (int i = 0; i < num_elements; i++) {matrix->elements[i].value = values[i];matrix->elements[i].row = row_indices[i];matrix->elements[i].col = col_indices[i];matrix->col_indices[i] = col_indices[i];matrix->row_ptr[row_indices[i]]++;}int sum = 0;for (int i = 0; i <= matrix->rows; i++) {int temp = matrix->row_ptr[i];matrix->row_ptr[i] = sum;sum += temp;}
}void setElement(CSRMatrix* matrix, int row, int col, int value) {if (row < 0 || row >= matrix->rows) {printf("Invalid row index.\n");return;}int index = matrix->row_ptr[row];matrix->elements[index].row = row;matrix->elements[index].col = col;matrix->elements[index].value = value;matrix->col_indices[index] = col;matrix->row_ptr[row]++; // 递增索引值
}void printCSRMatrix(CSRMatrix matrix) {printf("CSR Matrix:\n");printf("Rows: %d, Cols: %d, Num Elements: %d\n", matrix.rows, matrix.cols, matrix.num_elements);printf("Values: ");for (int i = 0; i < matrix.num_elements; i++) {printf("%d ", matrix.elements[i].value);}printf("\n");printf("Row Pointer: ");for (int i = 0; i <= matrix.rows; i++) {printf("%d ", matrix.row_ptr[i]);}printf("\n");printf("Column Indices: ");for (int i = 0; i < matrix.num_elements; i++) {printf("%d ", matrix.col_indices[i]);}printf("\n");
}
void printMatrixForm(CSRMatrix matrix) {printf("Matrix Form:\n");for (int i = 0; i < matrix.rows; i++) {for (int j = 0; j < matrix.cols; j++) {int value = 0;for (int k = matrix.row_ptr[i]; k < matrix.row_ptr[i + 1]; k++) {if (matrix.elements[k].col == j) {value = matrix.elements[k].value;break;}}printf("%d ", value);}printf("\n");}
}void destroyCSRMatrix(CSRMatrix* matrix) {free(matrix->elements);free(matrix->row_ptr);free(matrix->col_indices);matrix->elements = NULL;matrix->row_ptr = NULL;matrix->col_indices = NULL;
}int main() {int rows = 9;int cols = 3;int num_elements = 5;CSRMatrix matrix = createCSRMatrix(rows, cols, num_elements);int col_indices[] = {0, 0, 0, 0, 1};int row_indices[] = {3, 5, 7, 8, 7};int values[] = {2, 1, 3, 1, 4};initializeCSRMatrix(&matrix, values, row_indices, col_indices, num_elements);printCSRMatrix(matrix);printMatrixForm(matrix);destroyCSRMatrix(&matrix);return 0;
}相关文章:
【数据结构】数组和字符串(五):特殊矩阵的压缩存储:稀疏矩阵——压缩稀疏行(CSR)
文章目录 4.2.1 矩阵的数组表示4.2.2 特殊矩阵的压缩存储a. 对角矩阵的压缩存储b~c. 三角、对称矩阵的压缩存储d. 稀疏矩阵的压缩存储——三元组表e. 压缩稀疏行(Compressed Sparse Row,CSR)矩阵结构体创建CSR矩阵元素设置初始化打印矩阵销毁…...
springboot整合postgresql
使用docker安装postgres 简单起见,这里用docker来安装postgresql docker pull postgresdocker run --name postgres \-e POSTGRES_PASSWORD123456 \-p 5432:5432 \-v /usr/local/docker/postgresql/data:/var/lib/postgresql/data \-d postgrespostgres客户端 pg…...
C#__委托delegate
委托存储的是函数的引用(把某个函数赋值给一个委托类型的变量,这样的话这个变量就可以当成这个函数来进行使用了) 委托类型跟整型类型、浮点型类型一样,也是一种类型,是一种存储函数引用的类型 using System.Reflec…...
Jupyter Notebook的安装方法以及生成ipykernel
1. 安装Python和pip Jupyter Notebook是基于Python的,因此首先需要在电脑上安装Python和pip。可以通过访问Python官网(Welcome to Python.org)下载安装包进行安装。在安装Python的过程中,会提示是否安装pip,选择安装即可。 2. 安装Jupyter …...
测试员如何快速复现bug?一款合适的视频录制软件了解一下
你是不是还在为描述缺陷复现步骤而苦恼?你是不是还在为寻找一款合适的视屏录制软件而挣扎?那么,你应该好好看看这篇小文章。 作为测试人员,撰写测试用例、提交测试缺陷是基本工作。但往往我们会遇到:开发人员无法根据…...
论文-分布式-并发控制-并发控制问题的解决方案
目录 参考文献 问题 解法与证明 易读版本 参考文献 Dijkstra于1965年发表文章Solution of a Problem in Concurrent Programming Control,引出并发系统下的互斥(mutual exclusion)问题,自此开辟了分布式计算领域Dijkstra在文中给出了基于共享存储原子…...
【网络协议】聊聊http协议
当我们输入www.baidu.com的时候,其实是先将baidu.com的域名进行DNS解析,转换成对应的ip地址,然后开始进行基于TCP构建三次握手的连接,目前使用的是1.1 默认是开启了keep-Alive。可以在多次请求中进行连接复用。 HTTP 请求的构建…...
图神经网络论文笔记(一)——北邮:基于学习解纠缠因果子结构的图神经网络去偏
作者 :范少华 研究方向 :图神经网络 论文标题 :基于学习解纠缠因果子结构的图神经网络去偏 论文链接 :https://arxiv.org/pdf/2209.14107.pdf https://doi.org/10.48550/arXiv.2209.14107 大多数图神经网络(GNNs)通…...
java初始化list的几种方式
在Java中初始化List有以下几种常见的方式: 使用Arrays.asList()静态方法: List<Integer> list1 Arrays.asList(1, 2, 3);使用List接口的实现类ArrayList的构造函数: List<String> list2 new ArrayList<>();使用Collections.singletonList() String obj…...
Linux:文件操作
目录 一、关于文件 1、文件类的系统接口 2、文件的含义 二、文件操作 1、C语言文件相关接口 2、系统接口 open close write read 三、文件描述符 关于fd fd的分配规则 输出重定向示例 输入重定向示例 追加重定向示例 dup2函数 缓冲区 stdout与stderr perror…...
vue源码笔记之——运行时runtime
源码中的位运算 按位于 运算 if (shapeFlag & ShapeFlags.TELEPORT) {解释:如果shapFlag本身值为8,type为1的话,那么转换为二进制(js都是32位)那就是 shapFlag:00000000 00000000 00000000 00001000 …...
MySQL数据库干货_09—— MySQL中的外键约束(Foreign Key)
外键约束(Foreign Key) 添加外键约束 使用DDL语句添加外键约束 ALTER TABLE 表名 ADD CONSTRAINT 约束名 FOREIGN KEY( 列 名 ) REFERENCES 参照的表名(参照的列名);示例一: 创建 departments 表包含 department_id 、department_name ,location_id。…...
springboot配置https
SSL : secure socket layer 是一种加密协议,SSL主要用于保护数据在 客户端和服务器之间的传输,,防止未经授权的访问和窃取敏感信息 在腾讯云申请ssl证书 申请了之后在我的域名中,,解析 解析了之后&…...
java - IDEA IDE - 设置字符串断点
文章目录 java - IDEA IDE - 设置字符串断点概述笔记END java - IDEA IDE - 设置字符串断点 概述 IDE环境为IDEA2022.3 在看一段序列化的代码, 想找出报错抛异常那个点, 理解一下代码实现. 因为序列化代码实现在第三方jar包中, 改不了(只读的). 根本数不清第几次才会开始报…...
【图像分类】基于计算机视觉的坑洼道路检测和识别(ResNet网络,附代码和数据集)
写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。(专栏订阅用户订阅专栏后免费提供数据集和源码一份,超级VIP用户不在服务范围之内,不想订阅专栏的兄弟们可以私信…...
关于readline方法使用的一个中文乱码引发的思考
故事起源于这段代码,我想给一个本地地址然后去读取文件内容,然后使用了reader.readLine();方法,但是本地没有任何报错,但是线上中文乱码导致直接报错了。 BufferedReader reader;try {reader new BufferedReader(new FileReader(…...
BUUCTF 神秘龙卷风 1
BUUCTF:https://buuoj.cn/challenges 题目描述: 神秘龙卷风转转转,科学家用四位数字为它命名,但是发现解密后居然是一串外星人代码!!好可怕! 密文: 下载附件,解压得到一个.rar压缩…...
【JavaEE初阶】 认识文件与Java中操作文件
文章目录 🌴认识文件🚩树型结构组织和目录🚩文件路径(Path)🚩知识扩展 🎍Java 中操作文件🚩File 概述📌属性📌构造方法📌方法 🚩File使…...
数据结构───链表
花费一个周时间学完了链表(的一部分),简单总结一下。 链表的学习离不开画图,将其抽象成一种逻辑模型,可以减少思考时间,方便理解。 链表大致分为8种结构,自己学习并实现了两种结构,也…...
SQLAlchemy删除所有重复的用户|Counter类运用
Python标准库中的collections模块中的Counter类。Counter类用于计算可迭代对象中元素的出现次数,并以字典的形式返回结果,其中键是元素,值是该元素的出现次数。 for name, count in Counter(names).items() 是一个循环语句,它用于…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...
HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...
LINUX编译vlc
下载 VideoLAN / VLC GitLab 选择最新的发布版本 准备 sudo apt install -y xcb bison sudo apt install -y autopoint sudo apt install -y autoconf automake libtool编译ffmpeg LINUX FFMPEG编译汇总(最简化)_底部的附件列表中】: ffmpeg - lzip…...
Redis——Cluster配置
目录 分片 一、分片的本质与核心价值 二、分片实现方案对比 三、分片算法详解 1. 范围分片(顺序分片) 2. 哈希分片 3. 虚拟槽分片(Redis Cluster 方案) 四、Redis Cluster 分片实践要点 五、经典问题解析 C…...
