问题思考
还是紧接着上图的这个问题,比如,我们可以创建一个11 * 11的二维数组,其中0表示没有棋子,1表示黑子,2表示蓝子。。。当然是没有问题的。
但是如上图,我们发现,如果我们使用该二维数组,那么里面有很多值默认值是0,因此记录了很多没有意义的数据,所以我们便引入了 —— 稀疏数组。
简介
当一个数组中的大部分元素是0,或者为同一个值的时候,就可以使用稀疏数组来保存该数组。
当然,上面的解释可能会很抽象,我们先看下稀疏数组处理数据的方法:
- 稀疏数组的第一行记录数组一共有几行几列,有多少非0的不同的值
- 把非0的不同的值的元素的行、列、值记录在一个小规模的数组中,从而缩小程序的规模
看不懂没关系,因为它确实很抽象,我们来看图并举例说明 ↓
举例说明
- 如图,左边是一个矩阵,我们可以创建一个6 * 7的二维数组来表示它,但是同样的,我们也会发现里面会有很多是0的元素
- 那么我们就来创建一个稀疏数组,来看看稀疏数组在这种情况下,对应二维数组的优越性。
- 根据上面所说的稀疏数组的概念的第一条并结合左图的矩阵,我们知道,这个矩阵一共有6行7列,非0的不重复的元素个数一个是8个,所以,我们稀疏矩阵的第一行的数据便可以写出来了,也就是6 7 8
- 然后再根据概念的第二条并结合左图的矩阵:
- 举例:如左边矩阵中22这个值,它所在的行数是0,列数是3,值是22,原先二维数组中的这个元素可以在稀疏数组中表示为:0 3 22。
- 以此类推,其他的元素便也可以相应地放到稀疏数组中去。
转换完之后,我们得到的稀疏数组是9 * 3的,而原先未转换之前的二维数组是6 * 7 > 9 * 3,因此这个问题就因为稀疏数组得到了优化。
那么既然稀疏数组这么优秀,我们在解决问题的时候怎么把二维数组转换为稀疏数组呢?
或者简单的说:二维数组和稀疏数组在程序上是如何来实现的呢?
我们首先回到最开始的的棋盘问题 ↓
棋盘问题
然后我们再回到棋盘问题上来
需求
- 如下图,编写一个五子棋程序,有存盘退出和续上盘的功能。
思路
- 使用稀疏数组,来保留类似前面的二维数组(棋盘、地图等等)
- 把稀疏数组存盘,并且可以从新恢复原来的二维数组数
- 整体思路分析
- 我们可以创建一个11*11的二维数组,其中0表示没有棋子,1表示黑子,2表示蓝子。。。当然没有问题
- 既然我们认识到稀疏数组可以对这个11*11的二维数组做优化,我们就要考虑怎么把它转化为稀疏数组
分析
- 我们可以先来分析,11*11的二维数组转化之后的稀疏数组是什么样子的,然后再用程序来实现
- 根据上面的对二维数组和稀疏数组的举例说明,转化之后的稀疏数组如图所示,显然,原先的11 * 11的二维数组被简化为了3 * 3的稀疏数组,要简单的多了。下面我们来看看这个二维数组转换为稀疏数组的代码实现
代码实现(二维数组 → 稀疏数组)
- 话不多说,直接上代码
//二维数组转换为稀疏数组
@Test
public void test22() {
//创建一个原始的二维数组11*11
//其中0表示没有棋子,1表示黑子,2表示蓝子
int[][] chessArr = new int[11][11];
chessArr[1][2] = 1;
chessArr[2][3] = 2;
//输出原始的二维数组
System.out.println("原始的二维数组:");
for (int[] rows : chessArr) {
for (int data : rows) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//二维数组转换为稀疏数组
//稀疏数组
//1、第一行记录原先的二维数组一共有几行几列,有多少非0的不同的值
//2、把具有不同值的元素的行、列、值记录在一个小规模的数组中,从而缩小程序的规模
//分析:
//二维数组转换为稀疏数组,需要哪些数据
//1、需要先定义一个稀疏数组,确定原先的二维数组的行,列,不同的值的个数
//2、然后,把第一步获取的行、列,不同值的个数,赋值给稀疏数组的第一行
//3、然后,遍历原始的二维数组,获取数值的行、列,如果该数值不是0,赋值给稀疏数组
//实现:
//定义原始二维数组的行row
int row = 0;
//定义原始二维数组的列list
int list = 0;
//定义非0不同值的个数sum
int sum = 0;
for (int[] rows : chessArr) {
row++;
list = 0;
for (int data : rows) {
list++;
if (data != 0) {
sum++;
}
}
}
System.out.println("原始二维数组的非0的不同值的个数是: " + sum);
System.out.println("原始二维数组的行数是: " + row);
System.out.println("原始二维数组的列数是: " + list);
int[][] multiArr = new int[sum + 1][3];
multiArr[0][0] = row;
multiArr[0][1] = list;
multiArr[0][2] = sum;
//定义count,用于记录原先二维数组中的非0数据要放到稀疏数组中的哪一行
int count = 0;
for (int i = 0; i < chessArr.length; i++) {
for (int j = 0; j < chessArr[i].length; j++) {
if (chessArr[i][j] != 0) {
count++;
multiArr[count][0] = i;
multiArr[count][1] = j;
multiArr[count][2] = chessArr[i][j];
}
}
}
System.out.println();
//打印转换完成的稀疏数组
System.out.println("打印转换完成的稀疏数组: ");
for (int i = 0; i < multiArr.length; i++) {
System.out.printf("%d\t%d\t%d\t", multiArr[i][0], multiArr[i][1], multiArr[i][2]);
System.out.println();
}
}
打印结果
- 程序执行后,打印结果如下所示:
- ↑ 以上,我们便完成了二维数组 → 稀疏数组的转换
既然二维数组可以转换为稀疏数组,那么稀疏数组怎么转换为二维数组呢?
代码实现(稀疏数组 → 二维数组)
- OK!上代码!
//已知稀疏数组multiArr,把稀疏数组转换为二维数组
//分析
//把稀疏数组转换为二维数组,都需要哪些东西
//创建二维数组,我们需要知道这个二维数组的的行数、列数、以及非0元素的值的个数
// 而行数、列数、非0元素的值的个数我们都可以获取到,就是稀疏数组的第一行的三个数据:行数、列数、非0元素的值的个数
//然后创建完二维数组之后,我们需要给这个二维数组赋值
//怎么给这个二维数组赋值呢?
//需要遍历当前的稀疏数组,然后给二维数组赋值
int rows = multiArr[0][0];
int lists = multiArr[0][1];
System.out.println("从稀疏数组中获取到原二维数组的行数是: " + rows);
System.out.println("从稀疏数组中获取到原二维数组的行数是: " + lists);
int counts = 0;
int[][] chessArrTrans = new int[rows][lists];
//开始遍历稀疏数组
for (int i = 1; i < multiArr.length; i++) {
counts ++;
for (int j = 0; j < multiArr[i].length; j++) {
int transRow = multiArr[i][0];
int transList = multiArr[i][1];
chessArrTrans[transRow][transList] = multiArr[i][2];
}
System.out.println();
}
//打印转换完成的二维数组
System.out.println("打印转换完成的二维数组: ");
for (int[] chessArrTransRows : chessArrTrans) {
for (int chessArrTransData : chessArrTransRows) {
System.out.printf("%d\t", chessArrTransData);
}
System.out.println();
}
打印结果
- 程序执行后,打印结果如下所示:
- ↑ 以上,我们便完成了稀疏数组 → 二维数组的转换
拓展
要求:
- 在前面的基础上,将稀疏数组保存到磁盘上,比如 map.data
- 从稀疏数组恢复到原来的二维数组时,读取map.data 进行恢复