Java数据结构之稀疏数组
目录
- 线性结构与非线性结构
- 线性结构
- 非线性结构
- 稀疏数组
- 应用场景
- 代码实现
- 二维数组转稀疏数组
- 稀疏数组转二维数组
线性结构与非线性结构
线性结构
数据结构分两种,线性与非线性,线性结构的数据元素之间存在一对一的关系。
一对一指的是每个数据元素都有且仅有一个前驱元素和一个后继元素。元素之间存在严格顺序关系,每个元素都与唯一的一个元素相邻,一个元素的前一个元素就是它的前驱元素,后一个元素就是它的后继元素。确保元素在结构中的排列顺序。
举例来说,如果你有一个线性表(如数组或链表)包含整数元素 [1, 2, 3, 4, 5],那么元素1的前驱是空,后继是2;元素2的前驱是1,后继是3;元素3的前驱是2,后继是4。依此类推。
一对一的关系区分了线性结构与其他数据结构,如树或图,这些数据结构的元素之间可以有多个连接或关系。
关于线性结构的存储方式有两种,顺序存储与链式存储。
-
顺序存储(Sequential Storage): 线性结构的元素被存储在内存中一系列连续的存储单元中。这通常涉及使用数组来实现。每个元素都占用固定大小的内存空间,可以通过索引来访问元素。顺序存储适用于静态数据结构,元素的个数不会频繁地发生变化,因为插入或删除元素可能需要移动其他元素,效率较低。
举例:静态数组(如 Java 中的
int[])是一种顺序存储的线性结构。 -
链式存储(Linked Storage): 在链式存储中,线性结构的元素不一定是连续存储的,而是通过指针或引用相互连接在一起。每个元素通常包含数据和指向下一个元素的指针(或引用)。链式存储适用于动态数据结构,元素的插入和删除操作较为高效,因为不需要移动其他元素。
举例:链表是一种典型的链式存储的线性结构,包括单向链表、双向链表和循环链表等。
以下是一些常见的线性结构:
-
数组(Array): 数组是一种最基本的线性结构,其中元素在内存中按顺序连续存储。数组的大小通常是固定的,但在一些编程语言中也可以动态调整。数组允许随机访问元素,因为可以通过索引快速访问任何元素。
-
链表(Linked List): 链表是一种基于链式存储的线性结构,它由节点组成,每个节点包含数据和指向下一个节点的指针。链表分为单向链表、双向链表和循环链表等类型,允许高效的插入和删除操作,但访问元素的效率相对较低。
-
栈(Stack): 栈是一种特殊的线性结构,遵循后进先出(LIFO)原则。只能在栈顶进行插入(压栈)和删除(弹栈)操作,常用于表达式求值、递归函数调用和撤销功能等。
-
队列(Queue): 队列是一种基于先进先出(FIFO)原则的线性结构。元素从队列的一端(队尾)插入,从另一端(队首)删除。队列常用于调度、任务管理和广度优先搜索等算法。
-
双端队列(Deque): 双端队列是一种允许从两端插入和删除元素的线性结构,可以同时充当栈和队列的角色。
-
线性索引表(Index List): 线性索引表包括线性表的基本结构,但具有额外的索引,使得可以更快速地查找和访问元素。
-
线性堆(Heap): 堆是一种特殊的树状线性结构,通常分为最小堆和最大堆。堆被广泛用于实现优先队列和堆排序等算法。
这些线性结构在计算机科学和编程中都有广泛的应用,每种结构都适用于不同的问题和场景,根据需求选择合适的数据结构可以提高算法的效率和代码的可读性。
非线性结构
非线性结构是元素之间的关系不是一对一的,元素之间可以有多对多的关系,形成更为复杂的连接模式。这些结构通常不是基于单一直线的排列,而是呈现出分支或网状的结构。
一些常见的非线性结构包括:
-
树(Tree): 由节点组成,每个节点可以有一个父节点和零个或多个子节点。树被广泛用于层次化数据的表示,例如文件系统、组织结构等。特殊的树包括二叉树、二叉搜索树、平衡树、红黑树等。
-
图(Graph): 图是一种包含节点和边的非线性结构,节点之间的连接关系可以是多对多的,没有固定的层次结构。图用于表示各种复杂关系,如社交网络、网络拓扑、路线图等。特殊的图包括有向图、无向图、加权图、有向无环图(DAG)等。
-
堆(Heap): 堆虽然通常被视为线性结构的一种,但它实际上也是非线性结构。堆是一种特殊的树状结构,用于实现优先队列和堆排序等算法。最小堆和最大堆是堆的常见变种。
-
多维数组: 多维数组可以被视为非线性结构,因为元素之间的关系不仅仅是线性的。例如,二维数组是一个具有行和列的非线性结构,元素通过两个索引进行访问。
非线性结构在解决复杂的问题和表示多样化的数据关系时非常有用,但它们通常比线性结构更复杂,因此在访问和操作上可能需要更多的计算资源和算法。选择合适的数据结构取决于问题的性质和需求。
稀疏数组
应用场景
玩五子棋游戏,会有一个存档的功能,如果将盘上的所有的点都存下来会影响性能,这个时候可以通过稀疏数组来压缩棋盘来存储对应的位置,精确记录非默认值元素的信息,以节省存储空间和处理效率。
稀疏数组SparseArray用于表示大多数元素值为相同默认值(通常是0有可能是其它的默认值)的数组。
稀疏数组通常由三个部分组成:
-
原始数组的大小: 原始数组的行数和列数。
-
非默认值元素的个数及其位置信息: 存储非默认值元素的值、行号和列号。
-
默认值: 用于填充原始数组中未包含在稀疏数组中的位置。
虽然稀疏数组可以用于表示稀疏矩阵等多维数据,但稀疏数组本身的元素存储通常是线性的,这使得它可以有效地表示和压缩大规模的多维数据。
稀疏数组在许多应用中都有广泛的用途,特别是当处理大型数据集中存在大量默认值的情况时。以下是一些常见的应用场景:
-
图像处理: 在数字图像处理中,图像通常由像素组成,而大多数图像中的像素都具有相同的默认颜色或灰度值。使用稀疏数组可以有效地表示图像,只存储非默认像素的颜色值。
-
文本处理: 文本文档中通常包含大量空白字符,对于稀疏文本,可以使用稀疏数组来存储文本内容,减少存储空间的需求。
-
稀疏矩阵: 在科学和工程领域,许多矩阵都是稀疏的,其中大多数元素为零。这种情况下,稀疏数组可以用于表示和处理这些矩阵,减少内存和计算资源的开销。例如,在有限元分析、线性代数和网络分析中经常使用稀疏矩阵。
-
地理信息系统(GIS): GIS 数据通常包括地理坐标点,但大部分地理空间中没有点数据。使用稀疏数组可以有效地存储地理坐标信息,减小数据文件的大小。
-
网络路由表: 在计算机网络中,路由表用于指导数据包的传输。由于互联网规模庞大,网络路由表往往是稀疏的,其中只有少数路由条目被激活。稀疏数组可以用于高效表示和检索路由信息。
-
机器学习和数据挖掘: 在某些机器学习和数据挖掘应用中,特征矩阵可能具有大量的零值。稀疏数组用于存储特征矩阵,以减小内存占用和加速算法执行。
-
数据库管理系统: 在数据库中,可以使用稀疏数组来表示具有大量空值或默认值的数据,以减小存储和查询开销。
这些场景中,稀疏数组可以显著减少存储和处理成本,提高效率,并帮助管理大规模数据集。因此,稀疏数组是许多数据处理和存储应用中的重要工具。
代码实现
根据下图,将对应的原始二维数组与稀疏数组的互相转换。

二维数组转稀疏数组
主要思路就是确定有效数据的个数,根据有效个数初始化稀疏数组,遍历原来的二维数组给稀疏数组赋值。以下是实现方法以及抽出了一个公共的方法。
package com.jektong.al.sparsearray;import java.util.Arrays;/*** 稀疏数组** @author jektong* @date 2023年10月19日 8:29*/
public class SparseArray {public static void main(String[] args) {// 构建出二维数组的模型int[][] sourceArray = new int[6][7];sourceArray[0][3] = 22;sourceArray[0][6] = 15;sourceArray[1][1] = 11;sourceArray[1][5] = 17;sourceArray[2][3] = -6;sourceArray[3][5] = 39;sourceArray[4][0] = 91;sourceArray[5][2] = 28;// 输出二维数组for (int[] ints : sourceArray) {for (int anInt : ints) {System.out.printf("%d\t", anInt);}System.out.println();}// 统计二维数组的有效数据个数 sum=8int sum = sum(sourceArray);// 根据长度定义稀疏数组,+1是因为第一行的放的是原始的数组的几行几列一共存多少数字int[][] spareArray = new int[sum + 1][3];// 给第一行赋值 [6 7 8] 6行7列8个数字spareArray[0][0] = 6;spareArray[0][1] = 7;spareArray[0][2] = sum;// 用一个计数位置来累加每个稀疏数组的行数int count = 0;// 给稀疏数组进行赋值for (int i = 0; i < 6; i++) {for (int j = 0; j < 7; j++) {// 不为0就开始赋值if (sourceArray[i][j] != 0) {count++;// 稀疏数组某行的第一个数 对应原来数组的行号索引spareArray[count][0] = i;// 稀疏数组某行的第二个数 对应原来数组的列号索引spareArray[count][1] = j;// 稀疏数组某行的第三个数 对应原来数组的元素值spareArray[count][2] = sourceArray[i][j];}}}// 遍历出稀疏数组for (int[] ints : spareArray) {for (int anInt : ints) {System.out.printf("%d\t", anInt);}System.out.println();}// convertSpareArray(sourceArray);}/*** 统计二维数组的有效数据个数*/public static int sum(int[][] array) {int sum = 0;for (int[] ints : array) {System.out.println(Arrays.toString(array[0]));// array[0]相当于表示第一行的列的个数for (int j = 0; j < array[0].length; j++) {if (ints[j] != 0) {sum++;}}}return sum;}/*** 将原来数组转为稀疏数组 这是一个公共方法** @param sourceArray 原始数组*/public static void convertSpareArray(int[][] sourceArray) {// 输出二维数组for (int[] ints : sourceArray) {for (int anInt : ints) {System.out.printf("%d\t", anInt);}System.out.println();}// 统计二维数组的有效数据个数 sum=8int sum = sum(sourceArray);// 根据长度定义稀疏数组,+1是因为第一行的放的是原始的数组的几行几列一共存多少数字int[][] spareArray = new int[sum + 1][3];// 给第一行赋值 [6 7 8] 6行7列8个数字spareArray[0][0] = sourceArray.length;spareArray[0][1] = sourceArray[0].length;spareArray[0][2] = sum;// 给稀疏数组进行赋值int count = 0;for (int i = 0; i < sourceArray.length; i++) {for (int j = 0; j < sourceArray[0].length; j++) {if (sourceArray[i][j] != 0) {count++;spareArray[count][0] = i;spareArray[count][1] = j;spareArray[count][2] = sourceArray[i][j];}}}// 遍历出稀疏数组for (int[] ints : spareArray) {for (int anInt : ints) {System.out.printf("%d\t", anInt);}System.out.println();}}
}
通常稀疏数组是以三元组的形式表示,我们可以使用一个包含自定义对象的列表或数组来封装这个三元组的行,列,值。
package com.jektong.al.sparsearray;public class SparseArray {public static void main(String[] args) {// 构建出二维数组的模型int[][] sourceArray = new int[6][7];sourceArray[0][3] = 22;sourceArray[0][6] = 15;sourceArray[1][1] = 11;sourceArray[1][5] = 17;sourceArray[2][3] = -6;sourceArray[3][5] = 39;sourceArray[4][0] = 91;sourceArray[5][2] = 28;SparseElement[] sparseArray = convertSparseArray(sourceArray);printSparseArray(sparseArray);}public static class SparseElement {int row;int col;int value;public SparseElement(int row, int col, int value) {this.row = row;this.col = col;this.value = value;}}public static SparseElement[] convertSparseArray(int[][] sourceArray) {int numRows = sourceArray.length;int numCols = sourceArray[0].length;int sum = 0;// 计算有效数据个数for (int[] ints : sourceArray) {for (int j = 0; j < numCols; j++) {if (ints[j] != 0) {sum++;}}}// 创建稀疏数组SparseElement[] sparseArray = new SparseElement[sum + 1];sparseArray[0] = new SparseElement(numRows, numCols, sum);int count = 1;for (int i = 0; i < numRows; i++) {for (int j = 0; j < numCols; j++) {if (sourceArray[i][j] != 0) {sparseArray[count] = new SparseElement(i, j, sourceArray[i][j]);count++;}}}return sparseArray;}public static void printSparseArray(SparseElement[] sparseArray) {for (SparseElement element : sparseArray) {System.out.println(element.row + "\t" + element.col + "\t" + element.value);}}
}
运行效果:

稀疏数组转二维数组
稀疏数组转二维数组相对简单,先确定第一行,然后遍历剩下的行数对原始数组进行赋值:
package com.jektong.al.sparsearray;/*** @author jektong* @date 2023年10月20日 18:58*/
public class SpareArrayConvert {public static void convertArray(int[][] spareArray){// 先确定原始数组是几行几列?有多少的有效数字int row = spareArray[0][0];int col = spareArray[0][1];// 初始化原始数组int[][] sourceArray = new int[row][col];// 遍历剩下的数组长度for(int i = 1; i <spareArray.length;i++){// 赋值sourceArray[spareArray[i][0]][spareArray[i][1]] = spareArray[i][2];}for (int[] ints : sourceArray) {for (int anInt : ints) {System.out.printf("%d\t",anInt);}}}
}
测试一开始的原始数组,并输出结果:

相关文章:
Java数据结构之稀疏数组
目录 线性结构与非线性结构线性结构非线性结构 稀疏数组应用场景 代码实现二维数组转稀疏数组稀疏数组转二维数组 线性结构与非线性结构 线性结构 数据结构分两种,线性与非线性,线性结构的数据元素之间存在一对一的关系。 一对一指的是每个数据元素都…...
迅为RK3568开发板RTMP推流之视频监控
1 搭建 RTMP 媒流体服务器 nginx-rtmp 是一个基于 nginx 的 RTMP 服务模块,是一个功能强大的流媒体服务器模块, 它提供了丰富的功能和灵活的配置选项,适用于构建各种规模的流媒体平台和应用。无论是搭建实时视频直播平台、点播系统或多屏互…...
利用CSRF或XSS攻击网站的例子
利用 CSRF 攻击网站的简单示例: 假设有一个在线银行应用,用户可以在其中执行转账操作。用户登录后,系统会生成一个包含转账信息的表单,用户需要填写表单来发起转账。这个表单如下所示: <form action"https:/…...
LeetCode讲解篇之113. 路径总和 II
文章目录 题目描述题解思路题解代码 题目描述 题解思路 深度优先遍历二叉树,遍历的同时记录路径,直到遍历到叶节点,若路径和为targetSum则添加到结果集中 题解代码 func pathSum(root *TreeNode, targetSum int) [][]int {var res make([…...
中国HR从业者现状是怎样的?应如何提升自己?
HR(Human Resource)解释为人力资源,现在统称为人力资源顾问,跟传统人事有本质区别。传统人事一般是和行政部做相类似的工作,比如招聘,培训,职员的考核,职员的薪酬,职员调动等。现代人力资源&…...
Promise笔记-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题
Promise笔记 1. 预备知识1.1 实例对象与函数对象1.2 两种类型的回调函数1. 同步回调2. 异步回调 1.3 JS中的异常error处理1. 错误的类型2. 错误处理(捕获与抛出)3. 错误对象 2.Promise的理解和使用2.1 Promise是什么1.理解Promise2.Promise 的状态3. Pro…...
计算机考研自命题(2)
1、C语言-字符串交替拼接 1、用C编程,将两个字符串数组存储实现交替连接如aaa和bbb两个字符连接成ababab 如aaa和baba 两个字符,连接成 abaaaba #include<stdio.h>/* 解题思路:将两个字符串交替拼接,定义三个数组࿰…...
ZKP6.1 Discrete-log-based Polynomial Commitments (Preliminary)
ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 6: Discrete-log-based Polynomial Commitments (Yupeng Zhang) Recall How to build an efficient SNARK? A polynomial commitment scheme A polynomial interactive oracle proof (IOP) SNARK for general circuits Plo…...
五金经营小程序商城的作用体现在哪
对消费者而言,如今线上购买五金是很多人的选择,传统线下购买,不仅需要跑路,而且店内未必有所需品,但线上平台则一目了然购买所需品,本地/外地均可以触达到,同时还可对用户/会员进行高效管理&…...
今年这行情,不会自动化的要做好心理准备了
李强是一名软件测试工程师,入行之后在一家小型公司工作了五年。这段时间里,他主要负责手工测试和一些简单的自动化测试工作。由于公司项目也相对简单,他逐渐陷入了工作的舒适区,没有积极追求新的知识和技能。 然而随着身边朋友发展…...
汽车保养笔记
汽车保养笔记 汽车小保养汽车大保养五油:机油变速箱油刹车油转向助力油离合器油 四滤:机油滤芯更换空气滤芯更换空调滤芯更换汽油滤芯更换 三水防冻液(水)玻璃水电瓶水 其他刹车片球头减震器火花塞 4S店的4大套路---没必要清洗节气门更换火花塞和高压线圈…...
【斗破年番】官方改编用心了,彩鳞怀孕并未删,萧潇肯定登场,真相在丹药身上
【侵权联系删除】 【文/郑尔巴金】 斗破苍穹年番动画已经更新了,相信不少人都感觉到不可思议,萧炎跟随美杜莎女王回蛇人族的剧情,居然魔改成这样。好好的腹中孕育出新生命,变成了陨落心炎残余能量,不及时处理的话&…...
英语——分享篇——每日200词——3201-3400
3201——air-conditioning——[eərkəndɪʃnɪŋ]——n.空调设备;vt.给…装上空调——air-conditioning——air-condition空调(熟词)ing鹰(谐音)——空调设备的噪音让鹰不得安宁——The trains dont even have proper air-conditioning, grumbles Mr So. ——地铁…...
合并区间(C++解法)
题目 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:intervals …...
CUDA学习笔记(十四) Constant Memory
转载至https://www.cnblogs.com/1024incn/tag/CUDA/ CONSTANT MEMORY constant Memory对于device来说只读但是对于host是可读可写。constant Memory和global Memory一样都位于DRAM,并且有一个独立的on-chip cache,比直接从constant Memory读取要快得多…...
使用MFC创建一个SaleSystem
目录 1、项目的创建: 2、项目的配置: 3、设置窗口属性: (1)、设置图标 1)、添加导入资源 2)、代码初始化图标 (2)、设置标题 (3)、设置窗口…...
grafana v10.1版本设置告警
1. 相关概念概述 如图所示,点击切换菜单标志,可以看到警报相关子选项。 警报规则:通过PromQL语句定义告警规则,即达到怎样的状态触发告警。 联络点: 设置当警报规则实例触发时,如何通知联系人,…...
Python+Requests+PyTest+Excel+Allure 接口自动化测试实战
本文主要介绍了PythonRequestsPyTestExcelAllure 接口自动化测试实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 Unittest是Python标准库中自带的单元测试框架…...
日志分析系统——ELK
目录 一、ELK概述 ELK的组成 1、ElasticSearch 2、Logstash 3、Kiabana 完整日志采集系统基本特征 ELK的工作原理 二、ELK的部署 1、环境准备 2、部署ElasticSearch软件 3、安装Elasticsearch-head插件 4、Logstash部署 5、Kibana部署 三、FilebeatELK部署 1、安…...
Ubuntu小知识总结
Ubuntu相关的小知识总结 一、Ubuntu系统下修改用户开机密码二、Vmware虚拟机和主机之间复制、粘贴内容、拖拽文件的详细方法问题描述Vmware tools灰色不能安装解决方法小知识点:MarkDown的空格 三、Ubuntu虚拟机网络无法连接的几种解决方法1.重启网络编辑器2. 重启虚…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
