Java数据结构之稀疏数组

目录

    • 线性结构与非线性结构
      • 线性结构
      • 非线性结构
    • 稀疏数组
      • 应用场景
    • 代码实现
      • 二维数组转稀疏数组
      • 稀疏数组转二维数组

线性结构与非线性结构

线性结构

数据结构分两种,线性与非线性,线性结构的数据元素之间存在一对一的关系。

一对一指的是每个数据元素都有且仅有一个前驱元素和一个后继元素。元素之间存在严格顺序关系,每个元素都与唯一的一个元素相邻,一个元素的前一个元素就是它的前驱元素,后一个元素就是它的后继元素。确保元素在结构中的排列顺序。

举例来说,如果你有一个线性表(如数组或链表)包含整数元素 [1, 2, 3, 4, 5],那么元素1的前驱是空,后继是2;元素2的前驱是1,后继是3;元素3的前驱是2,后继是4。依此类推。

一对一的关系区分了线性结构与其他数据结构,如树或图,这些数据结构的元素之间可以有多个连接或关系。

关于线性结构的存储方式有两种,顺序存储与链式存储

  1. 顺序存储(Sequential Storage): 线性结构的元素被存储在内存中一系列连续的存储单元中。这通常涉及使用数组来实现。每个元素都占用固定大小的内存空间,可以通过索引来访问元素。顺序存储适用于静态数据结构,元素的个数不会频繁地发生变化,因为插入或删除元素可能需要移动其他元素,效率较低。

    举例:静态数组(如 Java 中的 int[])是一种顺序存储的线性结构。

  2. 链式存储(Linked Storage): 在链式存储中,线性结构的元素不一定是连续存储的,而是通过指针或引用相互连接在一起。每个元素通常包含数据和指向下一个元素的指针(或引用)。链式存储适用于动态数据结构,元素的插入和删除操作较为高效,因为不需要移动其他元素。

    举例:链表是一种典型的链式存储的线性结构,包括单向链表、双向链表和循环链表等。

以下是一些常见的线性结构:

  1. 数组(Array): 数组是一种最基本的线性结构,其中元素在内存中按顺序连续存储。数组的大小通常是固定的,但在一些编程语言中也可以动态调整。数组允许随机访问元素,因为可以通过索引快速访问任何元素。

  2. 链表(Linked List): 链表是一种基于链式存储的线性结构,它由节点组成,每个节点包含数据和指向下一个节点的指针。链表分为单向链表、双向链表和循环链表等类型,允许高效的插入和删除操作,但访问元素的效率相对较低。

  3. 栈(Stack): 栈是一种特殊的线性结构,遵循后进先出(LIFO)原则。只能在栈顶进行插入(压栈)和删除(弹栈)操作,常用于表达式求值、递归函数调用和撤销功能等。

  4. 队列(Queue): 队列是一种基于先进先出(FIFO)原则的线性结构。元素从队列的一端(队尾)插入,从另一端(队首)删除。队列常用于调度、任务管理和广度优先搜索等算法。

  5. 双端队列(Deque): 双端队列是一种允许从两端插入和删除元素的线性结构,可以同时充当栈和队列的角色。

  6. 线性索引表(Index List): 线性索引表包括线性表的基本结构,但具有额外的索引,使得可以更快速地查找和访问元素。

  7. 线性堆(Heap): 堆是一种特殊的树状线性结构,通常分为最小堆和最大堆。堆被广泛用于实现优先队列和堆排序等算法。

这些线性结构在计算机科学和编程中都有广泛的应用,每种结构都适用于不同的问题和场景,根据需求选择合适的数据结构可以提高算法的效率和代码的可读性。

非线性结构

非线性结构是元素之间的关系不是一对一的,元素之间可以有多对多的关系,形成更为复杂的连接模式。这些结构通常不是基于单一直线的排列,而是呈现出分支或网状的结构。

一些常见的非线性结构包括:

  1. 树(Tree): 由节点组成,每个节点可以有一个父节点和零个或多个子节点。树被广泛用于层次化数据的表示,例如文件系统、组织结构等。特殊的树包括二叉树、二叉搜索树、平衡树、红黑树等。

  2. 图(Graph): 图是一种包含节点和边的非线性结构,节点之间的连接关系可以是多对多的,没有固定的层次结构。图用于表示各种复杂关系,如社交网络、网络拓扑、路线图等。特殊的图包括有向图、无向图、加权图、有向无环图(DAG)等。

  3. 堆(Heap): 堆虽然通常被视为线性结构的一种,但它实际上也是非线性结构。堆是一种特殊的树状结构,用于实现优先队列和堆排序等算法。最小堆和最大堆是堆的常见变种。

  4. 多维数组: 多维数组可以被视为非线性结构,因为元素之间的关系不仅仅是线性的。例如,二维数组是一个具有行和列的非线性结构,元素通过两个索引进行访问。

非线性结构在解决复杂的问题和表示多样化的数据关系时非常有用,但它们通常比线性结构更复杂,因此在访问和操作上可能需要更多的计算资源和算法。选择合适的数据结构取决于问题的性质和需求。

稀疏数组

应用场景

玩五子棋游戏,会有一个存档的功能,如果将盘上的所有的点都存下来会影响性能,这个时候可以通过稀疏数组来压缩棋盘来存储对应的位置,精确记录非默认值元素的信息,以节省存储空间和处理效率。

稀疏数组SparseArray用于表示大多数元素值为相同默认值(通常是0有可能是其它的默认值)的数组。

稀疏数组通常由三个部分组成:

  • 原始数组的大小: 原始数组的行数和列数。

  • 非默认值元素的个数及其位置信息: 存储非默认值元素的值、行号和列号。

  • 默认值: 用于填充原始数组中未包含在稀疏数组中的位置。

虽然稀疏数组可以用于表示稀疏矩阵等多维数据,但稀疏数组本身的元素存储通常是线性的,这使得它可以有效地表示和压缩大规模的多维数据。

稀疏数组在许多应用中都有广泛的用途,特别是当处理大型数据集中存在大量默认值的情况时。以下是一些常见的应用场景:

  1. 图像处理: 在数字图像处理中,图像通常由像素组成,而大多数图像中的像素都具有相同的默认颜色或灰度值。使用稀疏数组可以有效地表示图像,只存储非默认像素的颜色值。

  2. 文本处理: 文本文档中通常包含大量空白字符,对于稀疏文本,可以使用稀疏数组来存储文本内容,减少存储空间的需求。

  3. 稀疏矩阵: 在科学和工程领域,许多矩阵都是稀疏的,其中大多数元素为零。这种情况下,稀疏数组可以用于表示和处理这些矩阵,减少内存和计算资源的开销。例如,在有限元分析、线性代数和网络分析中经常使用稀疏矩阵。

  4. 地理信息系统(GIS): GIS 数据通常包括地理坐标点,但大部分地理空间中没有点数据。使用稀疏数组可以有效地存储地理坐标信息,减小数据文件的大小。

  5. 网络路由表: 在计算机网络中,路由表用于指导数据包的传输。由于互联网规模庞大,网络路由表往往是稀疏的,其中只有少数路由条目被激活。稀疏数组可以用于高效表示和检索路由信息。

  6. 机器学习和数据挖掘: 在某些机器学习和数据挖掘应用中,特征矩阵可能具有大量的零值。稀疏数组用于存储特征矩阵,以减小内存占用和加速算法执行。

  7. 数据库管理系统: 在数据库中,可以使用稀疏数组来表示具有大量空值或默认值的数据,以减小存储和查询开销。

这些场景中,稀疏数组可以显著减少存储和处理成本,提高效率,并帮助管理大规模数据集。因此,稀疏数组是许多数据处理和存储应用中的重要工具。

代码实现

根据下图,将对应的原始二维数组与稀疏数组的互相转换。
在这里插入图片描述

二维数组转稀疏数组

主要思路就是确定有效数据的个数,根据有效个数初始化稀疏数组,遍历原来的二维数组给稀疏数组赋值。以下是实现方法以及抽出了一个公共的方法。

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);}}}
}

测试一开始的原始数组,并输出结果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线框图软件:Balsamiq Wireframes mac中文介绍

Balsamiq Wireframes mac是一款用于创建线框图的软件工具。它旨在帮助用户快速制作出清晰、简洁的界面原型&#xff0c;以便在设计和开发过程中进行协作和沟通。 Balsamiq Wireframes具有简单直观的用户界面&#xff0c;使用户能够快速添加和编辑各种用户界面元素&#xff0c;如…

当年很流行,现在已经淘汰的前端技术有哪些?

近几年&#xff0c;前端技术真可谓是飞速发展&#xff0c;不断有新的技术涌现&#xff0c;爆火的前端框架 Astro&#xff0c;前端运行时 Bun&#xff0c;构建工具 Vite 等都给前端提供了强大动力。当然&#xff0c;也有很多前端技术随着技术的发展不再需要使用&#xff0c;有了…

【软考】6.1 信息安全及技术

《信息安全和信息系统安全》 信息安全系统的体系架构 网络安全空间五大要素&#xff1a;认证、权限、完整、加密、不可否认&#xff08;抵赖&#xff09; 信息安全含义及属性 信息安全三要素&#xff1a;保密性、完整性、可用性最小授权原则&#xff1a;该有什么权限&#xf…

qwen大模型,推理速度慢,单卡/双卡速度慢,flash-attention安装,解决方案

场景 阿里的通义千问qwen大模型&#xff0c;推理速度慢&#xff0c;单卡/双卡速度慢。 详细&#xff1a; 1、今日在使用qwen-14b的float16版本进行推理&#xff08;BF16/FP16) 1.1 在qwen-14b-int4也会有同样的现象 2、使用3090 24G显卡两张 3、模型加载的device是auto&#x…

Python树莓派开发

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

【网络爬虫 | Python】数字货币ok链上bitcoin大额交易实时爬取,存入 mysql 数据库

文章目录 一、网站分析二、js 逆向获取 X-Apikey三、python 调用 js 获取 X-Apikey四、python 爬虫部分五、mysql 数据库、日志、配置文件、目录结构六、结尾 一、网站分析 oklink&#xff1a;https://www.oklink.com/ btc 大额交易&#xff1a;https://www.oklink.com/btc/tx-…

吉利高端品牌领克汽车携手体验家,重塑智能创新的汽车服务体验

浙江吉利控股集团&#xff08;以下简称“吉利集团”&#xff09;始建于1986年&#xff0c;1997年进入汽车行业&#xff0c;一直专注实业&#xff0c;专注技术创新和人才培养&#xff0c;坚定不移地推动企业转型升级和可持续发展。现资产总值超5100亿元&#xff0c;员工总数超过…

openEuler 服务器安装 JumpServer (all-in-one 模式)

openEuler 服务器安装 JumpServer JumpServer 简介什么是 JumpServer &#xff1f;JumpServer 的各种类型资产JumpServer 产品特色或优势JumpServer 符合 4A 规范 JumpServer 系统架构应用架构组件说明 JumpServer 安装部署环境要求网络端口网络端口列表防火墙常用命令 在线脚本…

冒泡排序、插入排序、选择排序和快速排序的原理

下面是对冒泡排序、插入排序、选择排序和快速排序的原理的简要解释&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;冒泡排序是一种简单的排序算法。它通过多次迭代比较相邻的元素&#xff0c;并交换它们的位置&#xff0c;使得较大&#xff08;或较小&…

自动化测试08

Junit 为什么学了Selenium还需学习Junit Selenium自动化测试框架&#xff1b;Junit单元测试框架。 拿着一个技术写自动化测试用例&#xff08;Selenium3&#xff09; 拿着一个技术管理已经编写好的测试用例&#xff08;Junit5&#xff09; Junit相关的技术 Junit是针对Java的一…

前端学成在线项目详细解析二

12-banner区域-课程表布局 HTML布局 <div class"right"><h3>我的课程表</h3><div class"content">1</div> </div> CSS样式 /* 课程表 */ .banner .right {margin-top: 60px;width: 218px;height: 305px;background-…

每日一题 2316. 统计无向图中无法互相到达点对数(中等,图连通分量)

题目很简单&#xff0c;只要求出每个连通分量有多少个节点即可首先通过建立一个字典来表示每个节点的邻接关系遍历每个节点&#xff0c;并通过邻接关系标记在当前连通分量内的所有的点&#xff0c;这样就可以知道一个连通分量内有多少个点在这里我陷入了一个误区&#xff0c;导…