Java数据结构之稀疏数组的实现与应用
作者:兴趣使然黄小黄 发布时间:2023-12-04 22:46:41
1.稀疏数组引入
1.1 使用场景
笔者在课程设计中曾写过一个扫雷小游戏,为了便于讲解,我们来做个简化(实际比这个复杂),只考虑当前位置有雷与无雷两种状况:雷用1表示,非雷用0表示。则将当前状态用二维数组表示如下:
在右侧的二维数组中,很多都是0,即记录了很多没有意义的数据,因此,我们考虑使用稀疏数组进行存储结构的优化。
1.2 稀疏数组简介
当一个数组中的大部分元素都是0(或者为相同的某一值),可以考虑使用稀疏数组来优化保存。
而稀疏数组的基本处理方式为:
记录数组有几行几列,有几个有效值;
把有效值的元素的行、列及值记录在一个小规模的数组中,从而缩小程序的规模。
例如上述的二维数组用稀疏数组表示,可以创建一个n*3列的稀疏数组。其中,n为有效值个数加1,第一行用于存储二维数组的行数、列数及有效值的个数,便于恢复二维数组时使用。
而从第二行开始后的每一行,都记录某个有效值的所在行、列索引与值。
上述二维数组用稀疏数组表示如下:
稀疏数组的第n行 | row | col | val |
---|---|---|---|
0 | 10 | 10 | 8 |
1 | 0 | 4 | 1 |
2 | 0 | 8 | 1 |
3 | 1 | 5 | 1 |
4 | 1 | 7 | 1 |
5 | 4 | 8 | 1 |
6 | 5 | 5 | 1 |
7 | 7 | 2 | 1 |
8 | 8 | 7 | 1 |
以索引0那行,10 10 8为例:表示原二维数组有10行10列,共有8个有效值。
以索引1那行,0 4 1为例:即第一个有效值在原数组的第0行第4列(索引为0和4),值为1,以此类推…
2.稀疏数组的实现
2.1 案例概述
1.使用稀疏数组来保存前面1.1样例中的二维数组(见下图);
2.可以由稀疏数组恢复成二维数组。
2.2 思路分析
二维数组转稀疏数组:
遍历原始的二维数组,得到有效数据的个数 amount;
根据 amount 可以创建稀疏数组 sparseArray[amount+1][3];
将二维数组中的有效值存入稀疏数组中。
稀疏数组恢复二维数组:
先读取稀疏数组的第一行,根据第一行的数据,创建原始二维数组;
读取第二行及以后行的数据,按照每行的行、列、值恢复有效值,录入二维数组中。
2.3 代码实现
根据如上思路,逐步实现稀疏数组,详情可见代码注释
参考代码:
/**
* @author 兴趣使然黄小黄
* @version 1.0
* 二维数组转稀疏数组、稀疏数组还原二维数组的实现
*/
public class SparseArray {
public static void main(String[] args) {
//先创建原始二维数组
int[][] originalArray = new int[10][10];
originalArray[0][4] = 1;
originalArray[0][8] = 1;
originalArray[1][5] = 1;
originalArray[1][7] = 1;
originalArray[4][8] = 1;
originalArray[5][5] = 1;
originalArray[7][2] = 1;
originalArray[8][7] = 1;
//输出原始的二维数组
System.out.println("原始的二维数组:");
for (int[] row: originalArray) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//将二维数组转成稀疏数组
//1.遍历二维数组,得到非0的有效数据的个数
int amount = 0;
for (int i = 0; i < originalArray.length; i++) {
for (int j = 0; j < originalArray[i].length; j++) {
if (originalArray[i][j] != 0){
amount++;
}
}
}
System.out.println("amount = " + amount);
//2.创建对应的稀疏数组 sparseArray[amount+1][3], 并初始化稀疏数组第一行的数据
//第一行第一列: 原数组的行数 第一行第二列: 原数组的列数 第一行第三列: 原数组的有效数据个数
int[][] sparseArray = new int[amount + 1][3];
sparseArray[0][0] = 10;
sparseArray[0][1] = 10;
sparseArray[0][2] = amount;
//3.遍历二维数组,将非0值存储进稀疏数组
int count = 0; //用于记录是第几个非零数据
for (int i = 0; i < originalArray.length; i++) {
for (int j = 0; j < originalArray[i].length; j++) {
if (originalArray[i][j] != 0){
count++;
sparseArray[count][0] = i; //记录所在行
sparseArray[count][1] = j; //记录所在列
sparseArray[count][2] = originalArray[i][j]; //记录值
}
}
}
//输出稀疏数组
System.out.println("稀疏数组:");
for (int i = 0; i < sparseArray.length; i++) {
System.out.printf("%d\t%d\t%d\n", sparseArray[i][0], sparseArray[i][1], sparseArray[i][2]);
}
//将稀疏数组恢复成为二维数组
//1.根据稀疏数组的第一行初始化二维数组
int[][] recoveredArray = new int[sparseArray[0][0]][sparseArray[0][1]];
//2.读取稀疏数组的后面行数据,赋值给原始二维数组
for (int i = 1; i < sparseArray.length; i++) {
recoveredArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
//输出二维数组
System.out.println("恢复后的二维数组:");
for (int[] row: recoveredArray) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
}
}
运行结果:
原始的二维数组:
0 0 0 0 1 0 0 0 1 0
0 0 0 0 0 1 0 1 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0
amount = 8
稀疏数组:
10 10 8
0 4 1
0 8 1
1 5 1
1 7 1
4 8 1
5 5 1
7 2 1
8 7 1
恢复后的二维数组:
0 0 0 0 1 0 0 0 1 0
0 0 0 0 0 1 0 1 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0
比对结果如下:
经过比较,恢复后的二维数组与原二维数组保持一致!
分析可知:原二维数组需要的空间为:10 * 10 *(int),而稀疏数组只需要 9 * 3 * (int),相对节省了73(int)的空间!
来源:https://blog.csdn.net/m0_60353039/article/details/127422549


猜你喜欢
- java获取文件的inode标识符,如果文件被删除或者重命名,inode的值会发生变更,因此可以在第一次加载File之后记录inode,后续
- android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是
- 详解Android Webview加载网页时发送HTTP头信息当你点击一个超链接进行跳转时,WebView会自动将当前地址作为Referer
- 方法参数public String listFireEvent(@Valid FireSearch fireSearch, Ht
- Ionic是一款流行的移动端开发框架,但是刚入门的同学会发现,Ionic在iOS和Android的底部tabs显示不一样。在安卓情况下底部t
- pom.xml与settings.xmlpom.xml与setting.xml,可以说是Maven中最重要的两个配置文件,决定了Maven的
- 这篇文章主要介绍了spring boot如何加入mail邮件支持,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 以前在公司做项目的时候,遇到了分辨率的适配问题,说起来当时挺纠结的,因为没有外网,所以这个问题,都是黑暗中摸索的,尝试了许多方法,最后和徒弟
- AsyncTask是Android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度
- 前言进入到 SpringBoot2.7 时代,有小伙伴发现有一个常用的类忽然过期了:在 Spring Security 时代,这个类可太重要
- 1、SerialPortHelper「Android串口通信」介绍原项目地址https://github.com/freyskill/Ser
- 由于项目这种类型的图片按钮比较多,所以重写了ImageButton类。package me.henji.widget;import andr
- 其实这个http下载器的功能已经相当完善了,支持:限速、post投递和上传、自定义http header、设置user agent、设置ra
- 1、首先导入solrj需要的的架包2、需要注意的是低版本是solr是使用SolrServer进行URL实例的,5.0之后已经使用SolrCl
- 本文实例讲述了C#实现集合转换成json格式数据的方法。分享给大家供大家参考,具体如下:/// <summary>/// dat
- 前后端分离的项目,前端有菜单(menu),后端有API(backendApi),一个menu对应的页面有N个API接口来支持,本文介绍如何基
- 注解类@Documented@Target({ElementType.METHOD})@Retention(RetentionPolicy.
- 本文实例讲述了Android实现彩信附件的添加与删除功能。分享给大家供大家参考,具体如下:添加附件在ComposeMessageActivi
- 本文主要从两个方面对Android Volley框架的使用方法进行讲解,具体内容如下一、网络请求1.get方式请求数据// 1 创建一个请求
- 方法一:通过Theme.Translucent@android:style/Theme.Translucent@android:style/