稀疏数组
需求:编写五子棋程序,实现存盘退出和续上盘的功能
棋盘是一个11行11列的棋盘,棋盘中有一个黑棋子和一颗蓝棋子
要求:保留已有的棋盘
思路:将棋盘用二维数组记录下来,0表示没棋子,1表示黑子,2表示蓝子,形成如下图的二维数组,详情看图
画图分析:
问题:如何将这个棋盘从二维数组转为稀疏数组?
答:因为该二维数组有很多元素值都为0,因为记录了很多没意义的元素,此时就可以使用稀疏数组对二维数组进行压缩
稀疏数组基本介绍:
当一个数组的元素大部分为0 (默认值)| 为同一个值的数组时,可以用稀疏数组来保存该数组
稀疏数组的处理方案:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值元素的行列及值记录在一个小规模的数组中,从而缩小程序规模
注:“小规模的数组”指的就是稀疏数组
稀疏数组是什么样的?
注:
- 稀疏数组第一行第一列记录原始二维数组的总行数,稀疏数组的第一行第二列记录原始二维数组的总列数,稀疏数组第一行第三列用来记录原始二维数组中有效数据的总个数。
- 稀疏数组除第一行之外,其下面的所有行都是用来记录原始二维数组中有效的数据所在的行、列和值
由以上可知:原先的6行7列 42 个元素的二维数组经过处理就变为了一个9行3列27个元素的稀疏数组,稀疏数组实现了对二维数组的压缩
稀疏数组的应用实例
-
使用稀疏数组,来保留二维数组(比如:棋盘、地图等)
-
把稀疏数组存盘,并且可以重新恢复到原来的二维数组
-
思路分析:(
重要
)-
二维数组转换为稀疏数组的步骤:
-
第一步,遍历原始的二维数组,得到要保存的有效数据的总个数,假设个数为sum,
有效数据指的就是二维数组中不同值的元素,并且不为0
问题:为什么要得到有效数据的个数呢?
答:因为只要知道了原始二维数组中有效数据的个数,我们就可以得知稀疏数组有多少行了,假设在原始二维数组中有效数据的总个数为n个,那稀疏数组就是一个3列(n+1)行的数组,1代表的就是稀疏数组中的第一行,第一行中第一、二、三列分别代表原始的二维数组中的总行数、总列数和有效数据的总个数。
画图分析:
-
第二步,然后根据sum就可创建就可创建稀疏数组 ,比如:
int[][] spareArr = new int[sum + 1][3]
稀疏数组的行数不固定,因为稀疏数组的行数随原始二维数组中有效数据的个数变化而变化
稀疏数组的列数是固定的,始终为三列
画图分析:
-
第三步,将二维数组中的有效数据存入稀疏数组中
-
第四步,最后将稀疏数组保存到磁盘中的具体某一个文件中(运用到了Java中的IO知识)
-
-
稀疏数组恢复成二维数组:
-
第一步,将具体的那某一个文件恢复成稀疏数组(运用到了Java中的IO知识)
-
第二步,先读取到稀疏数组中的第一行,根据稀疏数组中的第一行第一列、第一行第二列来 创建 二维数组
稀疏数组中的第一行第一列:记录的是原始二维数组中的总行数
稀疏数组中的第一行第二列:记录的是原始二维数组中的总列数
比如:
int [][] chessArr = new int [11][11];
-
第三步,将读取的稀疏数组中除第一行之外,其下面的所有行的数据,并且赋值给“第二步所创建的那个二维数组”中去
假设:在稀疏数组中记录了一个原始的二维数组
chessArr
中的一个有效的数据2 在第3行第4列,如何将2从稀疏数组中读取出来并赋值给原始的二维数组chessArr
?比如:
chessArr[2][3] = 2
画图分析:
-
-
-
代码实现:
public class SparseArray{ public static void main(String[] args){ // 先创建原始二维数组 // 0:表示没有棋子,1 : 表示黑子 ,2 : 表示蓝子 int chessArr[][] = new int[11][11]; /* int length = chessArr.length; System.out.println("长度为 length =" + length); // 11 */ chessArr[1][2] = 1; chessArr[2][3] = 2; System.out.printf("%s\n","原始的二维数组为:"); // 用增强for循环来输出原始的二维数组 for(int[] row : chessArr){ for(int element:row){ // 采用格式化输出 System.out.printf("%d\t",element); } // 每打印一行就换一行 System.out.println(); } // 以上完成了创建原始二维数组,接下来完成 将该二维数组转换为稀疏数组 // 第一步,使用for循环遍历原始的二维数组,得到原始二维数组中的有效数据的总个数,假设为sum int sum = 0; // sum用来记录原始的二维数组中的有效数据的总个数,假设为sum默认为0 for(int i = 0;i < chessArr.length;i++){ // 外侧循环用来控制行 for(int j = 0;j < chessArr.length;j++){ // 内侧循环用来控制列 if(chessArr[i][j] != 0){ // sum++; sum += 1; } } } System.out.println("原始二维数组中有效数据的总个数 sum = " + sum); // sum = 2 // 因为原始二维数组中chessArr中有两个有效数据,分别是1和2,所以这里sum最终统计出来的结果为2 // 第二步,以上操作获取到了原始二维数组中的有效数据的总个数,接下来就可创建对应的稀疏数组了 /** * 问题: 为什么确定了原始二维数组中的有效数据的总个数后就可以创建稀疏数组了呢? * 答:因为只要确定了原始二维数组中的有效数据的总个数后,假设总个数为sum个,我们就可知道稀疏数组的空间大小了 * 假设sum = 12; * 那么 稀疏数组的大小就是:int sparseArr[][] = new int[sum + 1][3]; * 稀疏数组的行是随着二维数组的有效数据的个数变化而变化,稀疏数组的列始终是3,固定不变 * */ int[][] sparseArr = new int[sum + 1][3]; // 根据之前分析,稀疏数组第一行第一列存的是原始二维数组的总行数,第一行第二列存的是原始二维数组的总列数,第一行第三列存的是原始二维数组的有效数据的总个数 sparseArr[0][0] = chessArr.length; // 第一行第一列 表示原始二维数组中的总行数 sparseArr[0][1] = chessArr.length;// 第一行第二列 表示的是原始二维数组中的总列数 sparseArr[0][2] = sum; // 第一行第三列表示原始二维数组中有效数据的总个数,sum中存的就是原始二维数组中有效数据的总个数 // 第三步,通过for循环遍历原始二维数组,将遍历得到的有效数据,存放到稀疏数组中,有效数据指的是数组中的元素为 不同值,并且不为0 int count = 0;// count 计数器,用来记录是第几个有效的数据,假设count的默认值为0 for(int i = 0;i< chessArr.length;i++){ // 外侧循环用来控制行 for(int j = 0;j < chessArr.length;j++){ // 内侧循环用来控制列 if(chessArr[i][j] != 0){ count++; /** * count是递增的,递增可以使用计数器来解决该问题 * 原因: * 因为第一个原始二维数组中的有效数据存放在稀疏数组的第2行,第二个存放在稀疏数组的第3行 */ sparseArr[count][0] = i; sparseArr[count][1] = j; sparseArr[count][2] = chessArr[i][j]; } } } // 输出稀疏数组的形式 System.out.println(); // 用来换行 System.out.println("得到的稀疏数组如下所示:"); // 通过for循环对稀疏数组进行遍历 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(); // 用来换行 // 至此,二维数组转换为稀疏数组就完成了,接下来完成稀疏数组恢复成二维数组 // 先将稀疏数组中的第一行第一列和第一行第二列读取出来,得到原始的二维数组的总行数和总列数 // 第一步,先读取稀疏数组中的第一行,根据第一行中记录的原始二维数组中的总行数、总列数来创建二维数组 int[][] newChessArr = new int[sparseArr[0][0]][sparseArr[0][1]]; // 第二步,通过for循环遍历读取稀疏数组中除第一行之外,其下面所有行的数据(也就是从第二行开始遍历),并且赋值给“第二步所创建的那个二维数组"中 /* 为什么要从第二行开始遍历? 因为稀疏数组中第一行存的是原始二维数组中的总行数、总列数和有效数据的总个数,所以要跳过对第一行的遍历,所以循环变量i = 1 */ for(int i = 1;i < sparseArr.lenth;i++){ // 无需判断是否为0,因为稀疏数组中存方法的都是有效数据 // 有效数据:指的是数组元素为不同值并且不为0 newChessArr[sparseArr[i][0][sparseArr[i][1]] = sparseArr[i][2]; } } }
总结:原始二维数组经过稀疏数组处理后数据量显然已变得很小了,达到了稀疏数组对二维数组的压缩效果,若在实际开发中使用这种小的技巧可以节省空间,提高效率!
课后练习:
- 在前面的基础上实现将稀疏数组保存到磁盘上,比如map.data
- 在"1."完成后的基础上,实现将“map.data”文件恢复成稀疏数组