如何写成高性能的代码(三):巧用稀疏矩阵节省内存占用

稀疏矩阵的概念

一个m×n的矩阵是一个由m行n列元素排列成的矩形阵列。矩阵里的元素可以是数字、符号及其他的类型的元素。

一般来说,在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。,下面的矩阵就是一个典型的稀疏矩阵。

我们日常使用的电子表格也是一个典型的稀疏矩阵场景,电子表格(SpreadJS, Excel,Google Sheet等等),整体看起来像一个table表格。,

其中列名称依次为A, B, C … …, 行名称依次为1, 2, 3 … …

举例一个比较极端的场景,在A1ZZ2000单元格分别赋值,这样我们就需要一个2000行,26*26+26=702列的矩阵来表示它,这个矩阵是一个明显的稀疏矩阵。

稀疏矩阵的存储方式及优化

直接存储为二维矩阵

直接使用二维矩阵会简单直接地存储整个电子表格,这样你不必每次都创建或删除一段内存。
但这是一种非常暴力的存储值的方法,这种方式下会消耗大量内容来存储毫无内容的单元格。
简单的来看一下它的复杂度:

  • 占用空间O(N2)
  • 插入数据需要破坏矩阵.
  • 删除数据需要破坏矩阵.
  • 搜索数据O(N2)
  • 访问数据O(1)

N是假设行和列具有相同长度并形成正方形矩阵的行/列数。

通过键值对(Map, Dictionary)优化

在这种方法中,只有在单元格有值时,我们才将单元格的值和位置存储在一起,使用HashMap或者Dictionary这些数据结构可以很容易的做到.。

下图我们可以看到,键值对中分别存储了单元格位置和单元格值。

来看一下它的复杂度:

  • 空间O(N)
  • 插入O(1)
  • 删除O(1)
  • 搜索O(N)
  • 访问O(1)

N为所记录的条目数。

通过稀疏矩阵存储方式优化

在稀疏矩阵中,我们可以使用三个不同的数组来存储行索引、列偏移、和其中的值,而不是直接在二维矩阵中存储值。以这种方式按列压缩稀疏矩阵

存储的三个数组:

  1.  =>单元格中的值。
  2. 行索引=>单元格的行索引。
  3. 列偏移=>这里每个索引都代表列,并且该数组将行开始的索引值存储在 Row 数组中。

稀疏矩阵具体的插入,、删除,、搜索,、访问的代码,大家可以自己来搜索,这方面的资料网上有很多。,这里不一一列举。

和上面一样,来看看这种方式的复杂度:

  • 空间O(N)
  • 插入O(N)
  • 删除O(N)
  • 搜索O(N)
  • 访问O(1)

相较于传统的数组存储或是键值对存储,稀疏矩阵存储构建了基于行索引为 Key 的数据字典,在松散布局的表格数据中,稀疏矩阵只会对非空数据进行存储,而不需要对空数据开辟额外的内存空间。使用这种特殊的存储策略,使得数据片段化变得容易,可以随时框取整个数据层中的一片数据,进行序列化或反序列化。如果我们在项目开发中需要存储类似结构的数据,稀疏矩阵这种存储方式,无论从时间还是空间上都能大大的提成性能。

在葡萄城的 SpreadJS GcExcel 表格组件中,也巧妙的使用了稀疏矩阵这一特性,可以随时替换或恢复整个存储结构中的任何一个级别的节点,以改变引用的方式更高效的地解决表格数据回滚和恢复问题,而这一点也为葡萄城表格组件支持多人在线协同打下了一个良好的基础。

由于底层采用了稀疏矩阵来优化存储,SpreadJS在前端页面中,实现了100W级别数据秒级响应的能力:

纯前端百万级数据请求、加载、筛选和排序

点击此处可以在线体验:性能演示

同时,结合SpreadJS中使用的Canvas 绘制模型,双缓存画布渲染等专利技术,让SpreadJS的前端性能相比于同类产品遥遥领先。

更多纯前端表格在线demo示例 :https://demo.grapecity.com.cn...

纯前端表格应用场景:https://www.grapecity.com.cn/...

移动端示例(可扫码体验):http://demo.grapecity.com.cn/...


葡萄城技术团队
葡萄城技术团队,分享技术文章。

葡萄城创建于1980年,是全球领先的软件开发技术和低代码平台提供商。以“赋能开发者”为使命,葡萄城致力...

2.5k 声望
20.7k 粉丝
0 条评论
推荐阅读
看看Angular有啥新玩法!手把手教你在Angular15中集成报表插件
摘要:本文由葡萄城技术团队于SegmentFault原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

葡萄城技术团队阅读 172评论 1

封面图
ESlint + Stylelint + VSCode自动格式化代码(2023)
安装插件 ESLint,然后 File -> Preference-> Settings(如果装了中文插件包应该是 文件 -> 选项 -> 设置),搜索 eslint,点击 Edit in setting.json

谭光志34阅读 20.8k评论 9

涨姿势了,有意思的气泡 Loading 效果
今日,群友提问,如何实现这么一个 Loading 效果:这个确实有点意思,但是这是 CSS 能够完成的?没错,这个效果中的核心气泡效果,其实借助 CSS 中的滤镜,能够比较轻松的实现,就是所需的元素可能多点。参考我们...

chokcoco24阅读 2.2k评论 3

你可能不需要JS!CSS实现一个计时器
CSS现在可不仅仅只是改一个颜色这么简单,还可以做很多交互,比如做一个功能齐全的计时器?样式上并不复杂,主要是几个交互的地方数字时钟的变化开始、暂停操作重置操作如何仅使用 CSS 来实现这样的功能呢?一起...

XboxYan25阅读 1.7k评论 1

封面图
在前端使用 JS 进行分类汇总
最近遇到一些同学在问 JS 中进行数据统计的问题。虽然数据统计一般会在数据库中进行,但是后端遇到需要使用程序来进行统计的情况也非常多。.NET 就为了对内存数据和数据库数据进行统一地数据处理,发明了 LINQ (L...

边城17阅读 2k

封面图
【代码鉴赏】简单优雅的JavaScript代码片段(一):异步控制
Promise.race不满足需求,因为如果有一个Promise率先reject,结果Promise也会立即reject;Promise.all也不满足需求,因为它会等待所有Promise,并且要求所有Promise都成功resolve。

csRyan26阅读 3.4k评论 1

「彻底弄懂」this全面解析
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在 哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性,会在 函数执行的过程中用到...

wuwhs17阅读 2.4k

封面图

葡萄城创建于1980年,是全球领先的软件开发技术和低代码平台提供商。以“赋能开发者”为使命,葡萄城致力...

2.5k 声望
20.7k 粉丝
宣传栏