2 稀疏数组
2.1 场景分析
编写的五子棋程序中,有存盘退出和续上盘的功能。
问题分析:
假设现在就下了两个棋子那是不是要使用二维数组将整个棋盘都存起来呢?
假设空的都为0,那对不是很多的值都是0, 因此记录了 很多没有意义的数据。
稀疏数组基本介绍:
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组或者说压缩原始二维数组的信息,降低冗余度。
稀疏数组的处理方法:
- 记录数组 一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而 缩小程序的规模
2.3 应用实例
需求:
将五子棋进行存档,以及稀疏数组与二维数组的互转
如上图,我们用0表示无子,1表示黑子,2表示蓝子。
将上述数据转换成稀疏数组的表现方式:
分析:
- ①表示原数组总行数
- ②表示原数组总列数
- ③表示原数组有多少个不一样的值
- 从第二列开始,指的是value在二维数组中的位置
经过稀疏数组的压缩之后,原数组从原来的11行11列变为了三行三列。
2.4 二维数组转稀疏数组
- 遍历原始的二维数组,得到要保存的有效元素个数
- 根据有效元素个数创建稀疏数组sparseArr
- 将二维数组的有效数据存入稀疏数组即可
设计一个将指定的二维数组转稀疏数组的方法:
// 设计一个将指定的二维数组转稀疏数组的方法
public static int[][] returnSparse(int[][] originalArray) {
// 操作之前先将数组遍历输出
System.out.println("原始二维数组为:");
for (int[] row : originalArray) {
for (int data : row) {
//"%"表示进行格式化输出,"%"之后的内容为格式的定义。
System.out.printf("%d\t", data);
}
System.out.println();
}
// 1、先遍历二维数组,得到非0数据的个数
int sum = 0;// 有效数据个数
for (int i = 0; i < originalArray[0].length; i++) {
for (int j = 0; j < originalArray[1].length; j++) {
if (originalArray[i][j] != 0) {
sum++;
}
}
}
/*
* 2、创建对应的稀疏数组
* 稀疏数组的列是固定的---3
* 稀疏数组的行等于有效个数+1,因为第一行是用来记录几行几列几个有效值
* */
int sparseArr[][] = new int[sum + 1][3];
// 3、给稀疏数组赋值
sparseArr[0][0] = originalArray[0].length;
sparseArr[0][1] = originalArray[1].length;
sparseArr[0][2] = sum;
// 4、遍历二维数组,将非0的数组存放到稀疏数组中
int count = 0;// count用于记录第几个非0数据
for (int i = 0; i < originalArray[0].length; i++) {
for (int j = 0; j < originalArray[1].length; j++) {
if (originalArray[i][j] != 0) {
count++;
sparseArr[count][0] = i;
sparseArr[count][1] = j;
sparseArr[count][2] = originalArray[i][j];
}
}
}
// 5、输出稀疏数组的形式
System.out.println();
System.out.println("二维数组--->稀疏数组:");
System.out.println("row col val");
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();
return sparseArr;
}
2.5 稀疏数组转原始二维数组
- 先读取稀疏数组的第一行,根据第一行的数据创建原始二维数组
- 读取稀疏数组后几行的数据,并赋给原始的二维数组即可
设计一个将稀疏数组转换成二维数组的方法:
// 设计一个将稀疏数组转换成二维数组的方法
public static int[][] returnArray(int[][] sparseArr) {
// 1、先读取稀疏数组的第一行,根据稀疏数组的第一行数据,创建原始数组
int originalArr[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
// 2、读取稀疏数组的后几行(从第二行开始),赋值给新建的二维数组即可
for (int i = 1; i < sparseArr.length; i++) {
originalArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
// 3、输出恢复后的二维数组
System.out.println("稀疏数组-->二维数组:");
for (int[] arr : originalArr) {
for (int result : arr) {
System.out.printf("%d\t", result);
}
System.out.println();
}
return originalArr;
}
2.6 我们将二维数组与稀疏数组之间的转换也设计成工具类
public class ChessUtils {
// 设计一个将指定的二维数组转稀疏数组的方法
public static int[][] returnSparse(int[][] originalArray) {
// 操作之前先将数组遍历输出
System.out.println("原始二维数组为:");
for (int[] row : originalArray) {
for (int data : row) {
//"%"表示进行格式化输出,"%"之后的内容为格式的定义。
System.out.printf("%d\t", data);
}
System.out.println();
}
// 1、先遍历二维数组,得到非0数据的个数
int sum = 0;// 有效数据个数
for (int i = 0; i < originalArray[0].length; i++) {
for (int j = 0; j < originalArray[1].length; j++) {
if (originalArray[i][j] != 0) {
sum++;
}
}
}
/*
* 2、创建对应的稀疏数组
* 稀疏数组的列是固定的---3
* 稀疏数组的行等于有效个数+1,因为第一行是用来记录几行几列几个有效值
* */
int sparseArr[][] = new int[sum + 1][3];
// 3、给稀疏数组赋值
sparseArr[0][0] = originalArray[0].length;
sparseArr[0][1] = originalArray[1].length;
sparseArr[0][2] = sum;
// 4、遍历二维数组,将非0的数组存放到稀疏数组中
int count = 0;// count用于记录第几个非0数据
for (int i = 0; i < originalArray[0].length; i++) {
for (int j = 0; j < originalArray[1].length; j++) {
if (originalArray[i][j] != 0) {
count++;
sparseArr[count][0] = i;
sparseArr[count][1] = j;
sparseArr[count][2] = originalArray[i][j];
}
}
}
// 5、输出稀疏数组的形式
System.out.println();
System.out.println("二维数组--->稀疏数组:");
System.out.println("row col val");
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();
return sparseArr;
}
// 设计一个将稀疏数组转换成二维数组的方法
public static int[][] returnArray(int[][] sparseArr) {
// 1、先读取稀疏数组的第一行,根据稀疏数组的第一行数据,创建原始数组
int originalArr[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
// 2、读取稀疏数组的后几行(从第二行开始),赋值给新建的二维数组即可
for (int i = 1; i < sparseArr.length; i++) {
originalArr[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
// 3、输出恢复后的二维数组
System.out.println("稀疏数组-->二维数组:");
for (int[] arr : originalArr) {
for (int result : arr) {
System.out.printf("%d\t", result);
}
System.out.println();
}
return originalArr;
}
// 设计一个将从磁盘获取的数据进行格式化的方法
public static int[][] sparseArrFormat(String[] sparse, int[][] sparseArr) {
int sum = 0;
for (int i = 3; i < sparse.length; i += 3) {
sum++;
sparseArr[sum][0] = Integer.parseInt(sparse[i]);
sparseArr[sum][1] = Integer.parseInt(sparse[i + 1]);
sparseArr[sum][2] = Integer.parseInt(sparse[i + 2]);
}
System.out.println("还原后的稀疏数组为:");
for (int i = 0; i < sparseArr.length; i++) {
System.out.printf("%d\t%d\t%d\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
}
return sparseArr;
}
}
说明:
从硬盘读取的数据显示效果不太好,因此设计了一个sparseArrFormat()方法进行格式化处理
2.7 将转换好的稀疏数组存入到磁盘以及从磁盘取出
这里我们将这个存和取的操作封装成工具类:
public class FileIOUtils {
// 将数据存入到磁盘名为map.data的文件中
public static void saveData(String filePath, int[][] sparseArr) {
System.out.println("将稀疏数组保存到磁盘并命名为map.data");
OutputStreamWriter osw = null;
FileOutputStream fos = null;
try {
File f = new File(filePath);
fos = new FileOutputStream(f);
osw = new OutputStreamWriter(fos, "UTF-8");
System.out.println("写入中----------");
for (int i = 0; i < sparseArr.length; i++) {
osw.write(sparseArr[i][0] + "," + sparseArr[i][1] + "," + sparseArr[i][2] + ",");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (osw != null) {
try {
osw.close();//关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();//关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("写入磁盘成功");
}
}
// 读取磁盘中的map.data文件
public static String[] readData(String filePath) {
System.out.println("数据读取中----------");
FileInputStream fis = null;
InputStreamReader isr = null;
String[] sb1 = null;
try {
fis = new FileInputStream(filePath);
isr = new InputStreamReader(fis, "UTF-8");
StringBuffer sb = new StringBuffer();
while (isr.ready()) {
sb.append((char) isr.read());
}
System.out.println("读取成功");
String ss = sb.toString();
sb1 = sb.toString().split(",");
//格式化输出
System.out.printf("从磁盘读取的字符串为:\n%s\n", ss);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isr != null) {
try {
isr.close();//关闭输入流
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();//关闭输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb1;
}
}
2.8 测试
public class TestMain {
public static void main(String[] args) {
// 先创建原始的二维数组11x11
// 0:表示没有棋子,1:表示黑子,2:表示蓝子
int chessArr[][] = new int[11][11];
chessArr[1][2] = 1;
chessArr[2][3] = 2;
// 将原始二维数组转换成稀疏数组
int[][] sparseArr = ChessUtils.returnSparse(chessArr);
System.out.println();
// 将稀疏数组转换成二维数组
int[][] originalArr = ChessUtils.returnArray(sparseArr);
System.out.println();
// 将稀疏数组存入到磁盘---spare.data
FileIOUtils.saveData("spare.data",sparseArr);
System.out.println();
// 将磁盘的spare.data格式的稀疏数组读取出来
String[] strings = FileIOUtils.readData("spare.data");
System.out.println();
System.out.println("将磁盘中的数据格式化中-----");
ChessUtils.sparseArrFormat(strings, sparseArr);
}
}
测试结果:
原始二维数组为:
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 2 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 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 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 0 0 0 0 0
二维数组--->稀疏数组:
row col val
11 11 2
1 2 1
2 3 2
稀疏数组-->二维数组:
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 2 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 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 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 0 0 0 0 0
将稀疏数组保存到磁盘并命名为map.data
写入中----------
写入磁盘成功
数据读取中----------
读取成功
从磁盘读取的字符串为:
11,11,2,1,2,1,2,3,2,
将磁盘中的数据格式化中-----
还原后的稀疏数组为:
11 11 2
1 2 1
2 3 2