基本介绍
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。
实际需求
相信大家都知道 “五子棋”,或者类似这种棋牌类游戏。它的棋盘就能看成一个二维数组,并且都有一个默认的值,当选手进行下棋操作的时候,会对此二维数组的某行某列的值进行修改。
棋盘与二维数组的对应关系如下图:
从上图可以看到,棋盘对应的二维数组中,没有进行操作的地方,默认值是0,如果程序把这种数据都记录上,是没有意义的。因此,这里就需要将上面的二维数组转换为 “稀疏数组”。
二维数组 转 稀疏数组
这张图就是转换之后的结果。
arr[0][0] = 原始数组的行数
arr[0][1] = 原始数组的列数
arr[0][2] = 有效数字的个数
其他值就存放对应的行列坐标和值。
接下来就用代码实现一下。
/**
* TODO 创建原始的二维数组
*/
private static int[][] createTwoArray() {
int[][] twoArray = new int[11][11];
twoArray[2][3] = 1;
twoArray[3][4] = 2;
return twoArray;
}
首先初始化原始数组,定义一个11行,11列的二维数组。这里就以棋子为例,黑子为 “1”,白子为 “2”,给定两个有效数据,然后进行稀疏数组的转换。
/**
* TODO 通过 原始的二维数组 得到 稀疏数组
*
* @param twoArray 原始的二维数组
*/
private static int[][] getSparseArray(int[][] twoArray) {
// 1. 先遍历二维数组 得到非0数据的个数
int sum = 0;
for (int[] oneArray : twoArray) {
for (int data : oneArray) {
if (data != 0) {
sum++;
}
}
}
System.out.println("非0数据的个数为:" + sum);
// 2. 创建对应的稀疏数组
int[][] sparseArray = new int[sum + 1][3];
// 3. 给稀疏数组第一行赋值(索引为 0)
sparseArray[0][0] = twoArray.length;
sparseArray[0][1] = twoArray[0].length;
sparseArray[0][2] = sum;
// 4. 遍历原始二维数组,将非0的值放入 稀疏数组中
int count = 0;
for (int i = 0; i < twoArray.length; i++) {
for (int j = 0; j < twoArray[i].length; j++) {
if (twoArray[i][j] != 0) {
count++;
sparseArray[count][0] = i;
sparseArray[count][1] = j;
sparseArray[count][2] = twoArray[i][j];
}
}
}
return sparseArray;
}
基本上代码都有注释,就不做过多的讲解了。编写测试类看看结果。
public static void main(String[] args) {
// 1. 得到原始的二维数组
int[][] twoArray = createTwoArray();
// 输出原始的二维数组
System.out.println("原始的二维数组:");
for (int[] oneArray : twoArray) {
for (int data : oneArray) {
System.out.print(data + "\t");
}
System.out.println();
}
// 打印分割线
printLine();
// 2. 将原始的二维数组变为稀疏数组
int[][] sparseArray = getSparseArray(twoArray);
// 输出稀疏数组
System.out.println("原始的二维数组转为稀疏数组是:");
for (int[] sparseOne : sparseArray) {
for (int data : sparseOne) {
System.out.print(data + "\t");
}
System.out.println();
}
}
输出结果:
原始的二维数组:
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 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数据的个数为:2
原始的二维数组转为稀疏数组是:
11 11 2
2 3 1
3 4 2
稀疏数组 转 二维数组
上面已经讲了二维数组转稀疏数组了,但是我们还需要将它转回原来的二维数组。这个就非常简单了,直接上代码。
/**
* TODO 将 稀疏数组 恢复成 二维数组
*
* @param sparseArray 稀疏数组
* @return void
*/
private static int[][] restoreTwoArray(int[][] sparseArray) {
// 1. 读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组
int[][] twoArray = new int[sparseArray[0][0]][sparseArray[0][1]];
// 2. 读取稀疏数组后几行的数据(从第二行开始),并赋值给 原始的二维数组
for (int i = 1; i < sparseArray.length; i++) {
twoArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
return twoArray;
}
同样通过测试类查看结果:
恢复之后的二维数组是:
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 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
完整代码
上面讲了 二维数组 和 稀疏数组 之间的转换。就拿“五子棋程序”来说,它还有其他额外的功能,例如:存档、读档、悔棋等等…通过下面这个简单的流程,实现简易版的 “五子棋程序”。
棋盘初始化 --> 二维数组 --> 稀疏数组 --> 存档(写入文件) --> 读档(读取文件) --> 稀疏数组 --> 二维数组 --> 棋盘
因为重点在稀疏数组,所以下面只是增加了文件存取的功能,并不涉及五子棋下棋的操作,还望各位读者理性阅读。
public class SparseArray {
public static void main(String[] args) {
// 1. 得到原始的二维数组
int[][] twoArray = createTwoArray();
// 输出原始的二维数组
System.out.println("原始的二维数组:");
for (int[] oneArray : twoArray) {
for (int data : oneArray) {
System.out.print(data + "\t");
}
System.out.println();
}
// 打印分割线
printLine();
// 2. 将原始的二维数组变为稀疏数组
int[][] sparseArray = getSparseArray(twoArray);
// 输出稀疏数组
System.out.println("原始的二维数组转为稀疏数组是:");
for (int[] sparseOne : sparseArray) {
for (int data : sparseOne) {
System.out.print(data + "\t");
}
System.out.println();
}
// 打印分割线
printLine();
// 3. 将 稀疏数组存入文件中 [存档功能]
System.out.println("将稀疏数组存入文件中");
outPutSparseArrayToFile(sparseArray);
// 打印分割线
printLine();
// 4. 读取 文件中的稀疏数组 [读档功能]
int[][] sparseArrayDataFromFile = inputSparseArrayFromFile();
System.out.println("从文件中读取的稀疏数组的值是:");
for (int[] oneArray : sparseArrayDataFromFile) {
for (int data : oneArray) {
System.out.print(data + "\t");
}
System.out.println();
}
// 打印分割线
printLine();
// 5. 将稀疏数组恢复成原始的二维数组
int[][] restoreTwoArray = restoreTwoArray(sparseArrayDataFromFile);
// 输出恢复之后的 二维数组
System.out.println("恢复之后的二维数组是:");
for (int[] oneArray : restoreTwoArray) {
for (int data : oneArray) {
System.out.print(data + "\t");
}
System.out.println();
}
}
/**
* TODO 创建原始的二维数组
*/
private static int[][] createTwoArray() {
int[][] twoArray = new int[11][11];
twoArray[2][3] = 1;
twoArray[3][4] = 2;
return twoArray;
}
/**
* TODO 通过 原始的二维数组 得到 稀疏数组
*
* @param twoArray 原始的二维数组
*/
private static int[][] getSparseArray(int[][] twoArray) {
// 1. 先遍历二维数组 得到非0数据的个数
int sum = 0;
for (int[] oneArray : twoArray) {
for (int data : oneArray) {
if (data != 0) {
sum++;
}
}
}
System.out.println("非0数据的个数为:" + sum);
// 2. 创建对应的稀疏数组
int[][] sparseArray = new int[sum + 1][3];
// 3. 给稀疏数组第一行赋值(索引为 0)
sparseArray[0][0] = twoArray.length;
sparseArray[0][1] = twoArray[0].length;
sparseArray[0][2] = sum;
// 4. 遍历原始二维数组,将非0的值放入 稀疏数组中
int count = 0;
for (int i = 0; i < twoArray.length; i++) {
for (int j = 0; j < twoArray[i].length; j++) {
if (twoArray[i][j] != 0) {
count++;
sparseArray[count][0] = i;
sparseArray[count][1] = j;
sparseArray[count][2] = twoArray[i][j];
}
}
}
return sparseArray;
}
/**
* TODO 将 稀疏数组 恢复成 二维数组
*
* @param sparseArray 稀疏数组
* @return void
*/
private static int[][] restoreTwoArray(int[][] sparseArray) {
// 1. 读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组
int[][] twoArray = new int[sparseArray[0][0]][sparseArray[0][1]];
// 2. 读取稀疏数组后几行的数据(从第二行开始),并赋值给 原始的二维数组
for (int i = 1; i < sparseArray.length; i++) {
twoArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
}
return twoArray;
}
/**
* TODO 将 稀疏 数组 存入 文件中(下棋中的 存档功能)
*
* @param sparseArray 稀疏数组
*/
private static void outPutSparseArrayToFile(int[][] sparseArray) {
FileWriter writer = null;
BufferedWriter bufferedWriter = null;
try {
writer = new FileWriter("D:/idea_workspace/my-project/algorithm-dataStructure/data-structure/src/file/a.txt");
bufferedWriter = new BufferedWriter(writer);
for (int[] oneArray : sparseArray) {
for (int data : oneArray) {
bufferedWriter.write(data + "\t");
}
bufferedWriter.write("\n");
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert bufferedWriter != null;
bufferedWriter.close();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* TODO 从 文件中 读取 稀疏数组(下棋中的 读档功能)
*/
private static int[][] inputSparseArrayFromFile() {
FileReader reader = null;
BufferedReader bufferedReader = null;
// 1. 读取的所有数据的集合
List<String> strLineList = new ArrayList<>();
try {
reader = new FileReader("D:/idea_workspace/my-project/algorithm-dataStructure/data-structure/src/file/a.txt");
bufferedReader = new BufferedReader(reader);
String line = "";
while ((line = bufferedReader.readLine()) != null) {
strLineList.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
assert reader != null;
reader.close();
assert bufferedReader != null;
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 2. 拿到第一行,初始化稀疏数组
String firstLineStr = strLineList.get(0);
String[] firstLineVal = firstLineStr.split("\t");
int[][] sparseArray = new int[strLineList.size()][firstLineVal.length];
// 3. 遍历 设置 稀疏数组的值
for (int i = 0; i < strLineList.size(); i++) {
String[] values = strLineList.get(i).split("\t");
for (int j = 0; j < values.length; j++) {
sparseArray[i][j] = Integer.parseInt(values[j]);
}
}
return sparseArray;
}
private static void printLine() {
System.out.println("-----------------------------------------");
}
}
输出结果,这里就不给出了,感兴趣的读者,直接复制完整代码运行即可。
谋事在人成事在天