稀疏数组及其应用举例
假设我们有这样一个需求,对于一个10x7的棋盘(如下图),我们需要存储每个棋子的具体坐标位置信息,尽可能少的占用内存。如果不采用稀疏数组的存储方式,那么我们需要一个int[10][8] 的内存空间约10x8x4 共320个字节。为了尽可能的减少存储空间,我们采用稀疏数组的方式来存储必要的信息即可。
采用的稀疏数组形如以下的格式:
行数 | 列数 | 需要记录的个数n |
---|---|---|
行1 | 列1 | 值1 |
行2 | 列2 | 值2 |
… | … | … |
行n | 列n | 值n |
这里,需要记录的个数代表有棋子的地方的个数。对于一个稀疏矩阵,需要记录的个数则是矩阵中非零元素(或其他指定的数值)的个数。稀疏矩阵指的是矩阵中大多数值都相同,对于这样的矩阵,我们只需要以稀疏数组的形式记录矩阵中不同的值,就可以大大的节省存储空间,特别是对于超大型矩阵。
一个很简单的例子,对于上图的棋盘,我们可以将其转换为以下的稀疏矩阵。我们用0代表大多数没有棋子的地方,用1代表白棋,用2代表黑棋。
10 | 8 | 11 |
---|---|---|
2 | 2 | 1 |
2 | 5 | 1 |
2 | 7 | 1 |
4 | 4 | 1 |
6 | 2 | 1 |
6 | 5 | 1 |
6 | 7 | 2 |
8 | 2 | 2 |
8 | 5 | 2 |
9 | 4 | 2 |
9 | 7 | 2 |
以上,我们用了一个int[n+1][3]的稀疏数组,这里n=11。共占用约11x3x4 = 132个字节。相比非稀疏存储(320字节),大大节省了存储空间。
下面,我们考虑一个原始的二维数组 和 对应稀疏数组 的相互转换。
二维数组->稀疏数组
根据以上棋盘数组的分析,我们很容易的知道需要以下转换步骤:
- 遍历原二维数组得到需要记录的非零元素个数N
- 根据得到的N来分配需要返回的稀疏数组的空间为int[N+1][3]
- 再次遍历原二维数组,对于非零元素,记录对应的行,列,值并存储到稀疏数组中。
Java代码
package com.like.java.data_structure.sparseArray;
// 原始二维数组转化为稀疏数组
public class Raw2Sparse {
/**
*
* @param rawArr 原始的二维数组
* @return sparseArr 对应的稀疏数组
*/
public static int[][] transfer(int [][] rawArr)
{
// 记录非零元素个数
int count = 0;
// 1. 遍历得到非零元素个数
for(int[] eachRow: rawArr)
{
for(int val:eachRow)
{
if(val != 0)
{
count ++;
}
}
}
// 2. 为稀疏数组分配内存空间
int[][] sparseArr = new int[count+1][3];
sparseArr[0][0] = rawArr.length;
sparseArr[0][1] = rawArr[0].length;
sparseArr[0][2] = count;
// 3. 再次遍历非零元素并记录到稀疏数组
int n = 0;
for(int i=0;i<rawArr.length;i++)
{
for(int j=0;j<rawArr[0].length;j++)
{
if(rawArr[i][j] !=0)
{
n ++;
sparseArr[n][0] = i;
sparseArr[n][1] = j;
sparseArr[n][2] = rawArr[i][j];
}
}
}
return sparseArr;
}
}
稀疏数组->原始二维数组
- 读取稀疏数组的第一行,获取原始二维数组的行数rowCount,列数colCount,非零元素个数
- 为原始的二维数组分配内存空间 rawArr = int[rowCount][colCount]
- 遍历稀疏数组剩余的记录,对应存储到rawArr 中。
Java代码
package com.like.java.data_structure.sparseArray;
// 稀疏数组转换为原始的二维数组
public class Spare2Raw {
/**
*
* @param spareArr 稀疏数组
* @return rawArr 原始的二维数组
*/
public static int[][] transfer(int[][] spareArr)
{
// 1. 获取行数,列数,非零元素个数
int row = spareArr[0][0];
int col = spareArr[0][1];
int count = spareArr[0][2];
// 2. 为原始二维数组分配内存空间
int[][] rawArr = new int[row][col];
// 3. 遍历稀疏数组,存储对应的值到原始二维数组中
for(int i=0;i<count;i++)
{
rawArr[spareArr[i+1][0]][spareArr[i+1][1]] = spareArr[i+1][2];
}
return rawArr;
}
}
测试用例Java
package com.like.java.data_structure.sparseArray;
import java.util.*;
import java.io.*;
public class Tester {
public static void main(String[] args) {
int[][] chessArr = new int[10][8];
for(int i=0;i<10;i++)
{
int row = (int)(Math.random()*chessArr.length);
int col = (int)(Math.random()*chessArr[0].length);
int val = (int)(Math.random()*10);
chessArr[row][col] = val;
}
// 原始数组转换为稀疏数组
int[][] sparseArr = Raw2Sparse.transfer(chessArr);
for(int i=0;i<sparseArr.length;i++)
{
System.out.println(Arrays.toString(sparseArr[i]));
}
// 稀疏数组转换为二位数组
int[][] rawArr1 = Spare2Raw.transfer(sparseArr);
System.out.println(Arrays.deepToString(rawArr1));
}
}