一、实际案例
如何高效的存储一局刚开始的棋局,并且在将来可以无损恢复
简单的方式将棋盘抽象成二维数据,未落子用0表示,黑子用1,白子用2;该问题就转换成如何持久化一个二维数组。序列化、IO皆可
s
p
a
r
s
e
=
[
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
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
0
0
0
0
0
0
0
0
0
]
sparse=\begin{bmatrix} 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 & 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 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ \end{bmatrix}
sparse=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡000000000000000000000001000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
这数学中上述矩阵被称为:稀疏矩阵,即具有很多相同的元素的矩阵,若不做任何处理直接存储显然不够高效。
二、基本介绍
2.1 压缩稀疏数组
如何高效的存储稀疏数组呢?
第一步:获取稀疏数组的规格,即行列信息
第二步:获取稀疏数组非0元素个数
第三步:创建压缩数组用于存储
根据上述的数组可得行列为11,非0元素个数为2,那么可以创建下列压缩数组
z
i
p
=
[
11
11
2
1
2
1
2
3
2
]
zip=\begin{bmatrix} 11 & 11 & 2 \\ 1 & 2 & 1 \\ 2 & 3 & 2 \\ \end{bmatrix}
zip=⎣⎡11121123212⎦⎤
解释:
zip
第一行记录spares
的行数、列数、非0元素个数;
zip
剩余每行第一列记录非0元素所在行,第二列记录非0元素所在列,第三列记录非0元素值
代码实现思路:
- 遍历稀疏数组获取有效数据个数
sum
- 根据
sum
创建压缩数组zip[sum+1][3]
- 将稀疏数组有效数据填入
zip
中
代码如下
// 压缩数组
public static int[][] zipArray(int[][] arr) {
// 遍历稀疏数组得到非0数据个数
int sum = 0;
for (int[] ints : arr) {
for (int j = 0; j < arr.length; j++) {
if (ints[j] != 0)
sum++;
}
}
// 创建压缩数组
int[][] zipArr = new int[sum + 1][3];
// 稀疏数组赋值
zipArr[0][0] = arr.length;
zipArr[0][1] = arr[0].length;
zipArr[0][2] = sum;
int line = 1;
// 再次遍历获取非0数据
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
if (arr[i][j] != 0) {
zipArr[line][0] = i;
zipArr[line][1] = j;
zipArr[line][2] = arr[i][j];
line++;
}
}
}
return zipArr;
}
2.2 解压稀疏数组
解压还原稀疏数组就很简单了
第一步:获取稀疏数组规格
第二步:从压缩数组中获取数据填入稀疏数组即可
代码实现思路:
- 读取压缩数组第一行,创建原始二维稀疏数组
- 读取压缩数组后几行赋值即可
代码如下
// 解压数组
public static int[][] unzipArray(int[][] arr) {
// 创建原始数组
int[][] srcArr = new int[arr[0][0]][arr[0][1]];
for (int i = 1; i < arr.length; i++) {
srcArr[arr[i][0]][arr[i][1]] = arr[i][2];
}
return srcArr;
}
三、补充
3.1 测试代码
package 稀疏数组;
/**
* Project: data structure
* Package: 稀疏数组
* Version: 1.0
* Author: wjun
* <p>
* Description:
* Created by hc on 2021/01/27 17:22
* © 1996 - 2021 Zhejiang Hong Cheng Computer Systems Co., Ltd.
*/
public class SparseArray {
public static void main(String[] args) {
int[][] sparseArr = new int[11][11];
sparseArr[1][1] = 1;
sparseArr[2][3] = 2;
int[][] zipArray = zipArray(sparseArr);
for (int[] ints : zipArray) {
for (int j = 0; j < zipArray.length; j++) {
System.out.print(ints[j] + "\t");
}
System.out.println();
}
int[][] unzipArray = unzipArray(zipArray);
for (int[] ints : unzipArray) {
for (int j = 0; j < unzipArray.length; j++) {
System.out.print(ints[j] + "\t");
}
System.out.println();
}
}
// 压缩数组
public static int[][] zipArray(int[][] arr) {
// 遍历稀疏数组得到非0数据个数
int sum = 0;
for (int[] ints : arr) {
for (int j = 0; j < arr.length; j++) {
if (ints[j] != 0)
sum++;
}
}
// 创建压缩数组
int[][] zipArr = new int[sum + 1][3];
// 稀疏数组赋值
zipArr[0][0] = arr.length;
zipArr[0][1] = arr[0].length;
zipArr[0][2] = sum;
int line = 1;
// 再次遍历获取非0数据
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[i][j] != 0) {
zipArr[line][0] = i;
zipArr[line][1] = j;
zipArr[line][2] = arr[i][j];
line++;
}
}
}
return zipArr;
}
// 解压数组
public static int[][] unzipArray(int[][] arr) {
// 创建原始数组
int[][] srcArr = new int[arr[0][0]][arr[0][1]];
for (int i = 1; i < arr.length; i++) {
srcArr[arr[i][0]][arr[i][1]] = arr[i][2];
}
return srcArr;
}
}
3.2 研究
问题:当有效数据数量达到多少时直接存储原始数组(此时成稀疏数组就不合适了)?
设原始数组为m x n
且有a
个有效数字,得到的压缩数据则为a+1 x 3
,有如下推导:
m
n
<
(
a
+
1
)
∗
3
=
>
a
<
m
n
3
−
1
mn < (a+1)*3 => a < \frac {mn} 3 - 1
mn<(a+1)∗3=>a<3mn−1
当有效数组接近整个数组的三分之一时,再使用该方法将得不偿失,且该方法本身就是以时间换取空间。