数据结构与算法(Java版) | 稀疏数组的一个实际应用案例

2023-05-16

提出需求:使用稀疏数组来保存类似棋盘或者地图等等映射而来的二维数组,然后存盘,并且希望可以重新恢复为原来的二维数组

通过上一讲的学习,我们知道了使用稀疏数组即可保存类似下面的二维数组,当然,现在大家可能都知道了该二维数组是由五子棋棋盘映射而来的,但是在实际应用中,它也有可能是由一个地图而映射来的哟!

在这里插入图片描述

于是,问题就来了,既然我们知道可以使用稀疏数组来保存类似上面的二维数组(比如棋盘或者地图等等)了,那么接下来我们是不是就可以考虑这样一个需求——将稀疏数组存盘,并且希望可以重新恢复为原来的二维数组了呢?

话不多说,下面我们就来分析一下这个需求。

还是以我们之前的五子棋棋盘为例,如下图所示,可以看到棋盘上有一个黑子和一个蓝子。

在这里插入图片描述

然后,我们将其映射成如下这样一个二维数组。

在这里插入图片描述

走到这一步,相信大家应该都没有什么问题吧!

然而,如果你仔细查看以上二维数组的话,那么你会发现它里面竟有一大堆的0(也即默认值),非常可惜,这些都是些没有意义的数据,因此接下来我们就要将以上二维数组转成一个稀疏数组了。这个稀疏数组长什么样呢?就长下面这样。

在这里插入图片描述

可以看到,这个稀疏数组总是一个行数不确定,但列数是3的这么一个动态数组。知道这个之后,下面我就要来给大家说一下这个稀疏数组的每一行所记录的究竟是些什么了。

  1. 第一行记录的是原始二维数组一共有几行几列,以及有多少个不同的值。

    很明显,原始二维数组一共有11行11列,2个有效数据,而且它们分别是1和2。

  2. 第二行记录的是第一个有效数据所在的行和列,以及其值。

    从以上原始二维数组中不难看出,第一个有效数据是1,且其在原始二维数组的第2行第3列上,也就是说它所在的行是1,列是2,相信这个大家都能看出来!

  3. 第三行记录的是第二个有效数据所在的行和列,以及其值。

    同上,这里我就不再赘述了。

至此,一个原始的11*11的二维数组经过处理就变成了一个3*3的稀疏数组。很明显,变小了,而这也刚好说明了使用稀疏数组的好处,即可以缩小原始二维数组的规模。

原始二维数组转变成稀疏数组之后,接下来我们就要考虑将稀疏数组再重新恢复成原始的二维数组了,是这样吧!不知道大家还记不记得五子棋游戏中那个续上局的操作,记得的话,那你就应该知道该操作所做的正是这样一件事,即重新将稀疏数组再恢复成原始的二维数组。

虽然我上面讲了这么多,但是我还是有一件事没跟大家讲明白,什么事呢,原始二维数组与稀疏数组它俩之间转换的思路,因此接下来我就要来给大家讲一讲这个思路了。

整体思路分析

原始二维数组转稀疏数组的思路

原始二维数组转稀疏数组的大体思路如下:

  1. 遍历原始二维数组,得到要保存的有效数据的个数,假定为sum

    为什么要得到有效数据的个数呢?因为我们只有在知道有效数据的个数之后才能知道将来的稀疏数组有多大。

  2. 根据sum创建稀疏数组int[sum + 1][3] sparseArr

    这个稀疏数组,大家应该知道它是行不确定(即sum + 1,取决于有效数据的个数),而列是固定(即3)的这个样子的吧!

  3. 将原始二维数组的有效数据存入到稀疏数组中。

    注意,稀疏数组的第一行还得存储原始二维数组的行和列,以及有多少个不同的值哟!

其实,以上有一个过程我给省略掉了,眼尖的同学应该能看出来,什么过程呢,就是稀疏数组存盘的这个过程,说白了,就是稀疏数组最终还是得保存到磁盘文件中,例如chess.data,很显然,这一过程会牵扯到IO。但是,由于我忽略掉了这一过程,所以我在代码中肯定就不会去实现它了,当然,不除非有部分同学会感兴趣,如果真是这样的话,那你就得自个私下去实现这一过程了。

稀疏数组转原始二维数组的思路

稀疏数组转原始二维数组的大体思路如下:

  1. 先读取稀疏数组的第一行,然后根据第一行记录的数据创建原始二维数组,比如我们就叫int chessArr2[11][11]
  2. 再读取稀疏数组后几行记录的数据,然后赋给原始二维数组。

注意,在将稀疏数组恢复成原始二维数组前,其实有一个过程我给忽略掉了,什么过程呢,就是你得先把稀疏数组存盘的那个文件读取为稀疏数组,然后你才能将稀疏数组恢复成原始二维数组,是这样吧!当然,不除非有部分同学会感兴趣,如果真是这样的话,那你就得自个私下去实现这一过程了。

代码实现

分析完毕原始二维数组转稀疏数组和稀疏数组转原始二维数组的大体思路之后,接下来我们就要准备用代码来实现这一思路了。

不过,在代码实现之前,有句话我还得给大家说一下,就是我们写一段代码,本身其实是并不难的,难的是之前的思路分析,就是你怎么想到能够这样去处理的,而这个就是算法的力量。总之,别人费劲巴拉设计出来一个数据结构之后,我们所需要做的只是将这种数据结构应用到实际的开发中,用以提高我们程序的一个效率,俗话说就是“如果说我看得远,那是因为我站在巨人的肩上”。

下面,我们就要正式来写代码将上面我们所分析的原始二维数组转稀疏数组和稀疏数组转原始二维数组的大体思路予以实现了。

package com.meimeixia.sparsearray;

/**
 * @author liayun
 * @create 2023-04-22 10:05
 */
public class SparseArray {

    public static void main(String[] args) {
        /**
         * 创建一个原始的二维数组。
         *
         * 注意,这个原始的二维数组是由五子棋盘映射而来的,而且我们还是这样来表示棋盘上的棋子的:
         * 0:表示没有棋子
         * 1:表示黑子
         * 2:表示蓝子
         */
        int chessArr1[][] = new int[11][11];
        chessArr1[1][2] = 1;
        chessArr1[2][3] = 2;
        chessArr1[4][5] = 2;
        // 原始二维数组创建好并赋值之后,我们便来输出一下它
        System.out.println("原始的二维数组就长这样:");
        for (int[] row : chessArr1) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }

        /**
         * 将原始二维数组转成稀疏数组。
         */
        // 1. 遍历原始二维数组,以期得到有效数据(即非0数据)的个数。
        // 不知道大家会不会有这样一个困惑,就是为啥非得先这样做呢?这是因为如果你不先通过遍历原始二维数组的方式把有效数据的个数得到,
        // 那么接下来你就无法创建出对应的稀疏数组了,除非你使用集合,但是这里我又要求大家只能使用数组,故大家就只能这样处理了!
        int sum = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if (chessArr1[i][j] != 0) {
                    sum++;
                }
            }
        }
        // 2. 创建对应的稀疏数组。
        int sparseArr[][] = new int[sum + 1][3];
        // 3. 为稀疏数组赋值。
        // (1) 不用我说,相信大家应该都知道稀疏数组的第一行记录的是原始二维数组的行、列以及有效数据的个数吧!
        sparseArr[0][0] = 11;
        sparseArr[0][1] = 11;
        sparseArr[0][2] = sum;
        // (2) 再次遍历原始二维数组,并将非0数据存放到稀疏数组中。
        int count = 0; // 计数器count用于记录存放的是第几个非0数据
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if (chessArr1[i][j] != 0) {
                    count++;
                    sparseArr[count][0] = i;
                    sparseArr[count][1] = j;
                    sparseArr[count][2] = chessArr1[i][j];
                }
            }
        }

        // 稀疏数组创建完毕之后,咱们再来输出一下,好给大家看看它究竟长啥样。
        System.out.println();
        System.out.println("得到的稀疏数组为:");
        for (int i = 0; i < sparseArr.length; i++) {
            System.out.printf("%d\t%d\t%d\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
        }

        System.out.println();

        /**
         * 将稀疏数组恢复成原始的二维数组。
         */
        // 1. 先读取稀疏数组的第一行,然后根据第一行记录的数据创建原始二维数组,比如我们就叫int chessArr2[11][11]。
        int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
        // 2. 再读取稀疏数组后几行记录的数据,然后赋给原始二维数组。
        for (int i = 1; i < sparseArr.length; i++) {
            chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
        }

        // 咱们输出一下恢复后的原始二维数组,看看它长什么样子。
        System.out.println();
        System.out.println("恢复后的原始二维数组长这样:");
        for (int[] row : chessArr2) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }

}

以上便是稀疏数组的一个应用实例。

我希望大家通过以上稀疏数组的应用实例能深刻理解这一点,就是原始二维数组在经过稀疏数组的处理之后,数据量相应地就会变得很小,如此一来,便能起到一个缩小原始二维数组规模的效果。当然,要是我们在实际开发中有遇到类似这样的需求,那么使用该小技巧无形中就能节省我们的内存空间,提高效率也说不定。

最后,有一点我还得给大家说一下,就是我写的以上代码并没有实现将稀疏数组保存到磁盘文件上这一功能,这一点相信大伙都能看得出来。于是,接下来你就有了这样一个任务,即在我上面写的代码基础之上,将稀疏数组保存到磁盘文件中,比如map.data;而至于恢复为原来的二维数组嘛,那你就得读取map.data文件进行恢复了。

以上任务难吗?不难,因为你当初学Java基础的时候肯定是学过IO流的,而这个任务仅仅只是需要用到一点点IO流的知识而已,你要是完成不了那就太说不过去了,该打屁股,哈哈哈😂,当然,这里我并没有去完成这样一个任务,主要是我觉得二维数组与稀疏数组之间转换的核心代码才是大家应该掌握的重点,至于以上任务嘛,那就纯纯是属于IO流的部分了,相信只要学过Java的同学应该都能完成,因为并不难啊!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构与算法(Java版) | 稀疏数组的一个实际应用案例 的相关文章

  • Archlinux配置邮件(以qq邮箱为例)

    Archlinux配置邮件 以qq邮箱为例 安装s nail span class token function sudo span pacman S s nail 配置SMTP发送邮件 开启IMAP SMTP服务 打开qq邮箱网页版 gt
  • 电子爱好者总结的28个电子行业技术网站

    以下是一位电子爱好者总结的28个电子行业技术网站 21IC 电子 http www 21IC COM 中国电子资源网 xff1a http www ec66 com 中国电子进修网 http www studydz com 电子设计技术网
  • S_OK,S_FALSE,E_FAIL

    今天在调试一个ICOP的操作的时候 xff0c 发现连接被动关闭的时候老是会在一处断言处失败 xff0c 跟了很久终于发现了问题 在此记录一下 xff1a 断言报错的代码如下 xff1a HRESULT CIoCPWorker UnregI
  • Win7 应用程序无法正常启动(0xc000000d)的解决方法

    自从重装了WIN7系统后 xff0c VS2010编译出来的项目程序就不能正常启动 xff0c 启动的时候总是提示 应用程序无法正常启动 xff08 0xc000000d xff09 请单击 确定 关闭应用程序 在网上查找了很多解决方案 x
  • MySQL存储过程where条件执行失败的问题

    前几天对服务器实体做了属性缓存机制 xff0c 当时测试也没有出现大的问题 xff0c 昨天有人跟我说 xff0c 登陆的时候角色等级显示错误 xff0c 我复测了一下 xff0c 发现不只是等级错误 xff0c 进入游戏后角色位置 金钱
  • 程序员与厨师

    不管你信不信 反正我是信了 每一个程序员上辈子都是呆在厨房的厨子 好吧 你不信 我来证明给你看 1 下厨前 你得知道做的是早餐还是中晚餐 中晚餐的话 怎么也得走趟超市 如遇到好友聚会 怎么着也得做出一桌对得起朋友的饭菜 还有你得分析 朋友中
  • VS2010/VS2012 设置全局头文件和库路径

    在VS2010之前 xff0c 设置项目的全局头文件和库路径是非常方便的 xff0c 直接选择菜单Tools gt Options gt Projects and Solutions gt VC 43 43 Directories xff0
  • Linux下rz/sz安装及使用方法

    新搞的云服务器用SecureCRT不支持上传和下载 xff0c 没有找到rz命令 记录一下如何安装rz sz命令的方法 一 工具说明 在SecureCRT这样的ssh登录软件里 通过在Linux界面里输入rz sz命令来上传 下载文件 对于
  • 关于mysql存储过程创建动态表名及参数处理

    转载请注明出处 xff1a 帘卷西风的专栏 http blog csdn net ljxfblog 最近游戏开始第二次内测 xff0c 开始处理操作日志 xff0c 最开始把日志放到同一个表里面 xff0c 发现一天时间 xff0c 平均1
  • 关于mysql自增id的获取和重置

    转载请注明出处 xff1a 帘卷西风的专栏 http blog csdn net ljxfblog mysql获取自增id的几种方法 使用max函数 xff1a select max id from tablename 优点 xff1a 使
  • 关于SQL中Union和Join的用法

    转载请注明出处 xff1a 帘卷西风的专栏 http blog csdn net ljxfblog 一直以来 xff0c 对于数据库SQL方面都是半吊子水平 xff0c 能写一些基本的增删改查的语句 xff0c 大部分时间都是用下Where
  • 使用Cmake生成跨平台项目编译解决方案

    项目最近有需求在windows下面运行 xff0c 我花了几周时间将linux的服务器移植到windows下面 xff0c 目前已经能够正常运行服务器 xff0c 目前又有了新需求 xff0c 两边的代码结构和组织是分开的 xff0c 因此
  • linux下shell技巧

    经常看到一些大牛操作linux的时候 xff0c 双手运指如飞 xff0c 指令如流水般输出 xff0c 会不会感到羡慕呢 xff1f 本文就整理了一些linux下shell的技巧 xff0c 保管你学会之后 xff0c shell输出ap
  • Cmake在windows支持预编译头文件(stdafx.h)

    最近一直在研究cmake构建项目 xff0c 之前接触cmake的时候就感觉不太喜欢cmake xff0c 觉得它太乱了 xff0c 产生了太多的中间文件 xff0c 产生的项目文件也不是特别友好 xff0c 在windows下 xff0c
  • win服务器设置开机自动登录

    之前设置了一个开机自动执行脚本 xff0c 发现重启服务器之后没有生效 xff0c 原因在于 xff0c 服务器重启之后 xff0c 不会自动登录用户 xff0c 因此没有执行脚本 xff1b 因此第一步先设置服务器启动之后自动登录用户 x
  • Zynq ZC702平台 QSPI + eMMC实现

    预备知识 xff1a UG821 The processor system boot is a two stage process Another boot mode supported through FSBL is eMMC boot
  • 什么是Vista?

    Vista是微软下一代操作系统 xff0c 以前叫做Longhorn xff08 微软当初内部的代号 xff09 7月22日微软对外宣布正式名称是Windows Vista 作为微软的最新操作系统 xff0c Windows Vista第一
  • Android识别模拟器,判断是模拟器还是真机

    文章目录 前言原理禁止模拟器安装apk代码识别验证最后 前言 对于android开发者来说 xff0c 模拟器是开发工具 xff0c 但是对用户来说 xff0c 可能就是薅羊毛 找漏洞的赚钱工具 不管是活动风控还是内容保护等等其他的出发点
  • Dataframe.info()显示空值与类型信息

    使用Dataframe info 默认不带参数只显示摘要信息 如果想显示空值信息与类型信息 testData span class token punctuation span info span class token punctuati
  • 创建WinPE启动盘、常用imagex指令、常用dism指令

    创建WinPE启动盘 常用imagex指令 常用dism指令 一 创建WinPE启动盘 1 准备工作 下载WAIK工具 xff1a WAIK下载页面 lesca使用的WAIK版本 xff1a KB3AIK EN iso copype cmd

随机推荐