1.概念
我们来观察以下这张表,假设这是一个二维数组
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 | 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 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 5 | 0 | 0 | 0 | 0 | 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 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
可以看到在这个二维数组中,数组的大小为10*14,我们只存储了部分数据,有大量的空间处于空闲状态,这就造成了一定的空间浪费。
那有没有一种能够能达到和这种存储结构不变又节省空间的方式呢?
我们可以通过稀疏数组来实现:
我们将下表也看做一个二维数组,这个二维数组有3列
- 第一列是存数据的行数的索引;
- 第二列存数据的列数的索引;
- 第三列存数据的值;
而在这张表的第一行中,每一列分别存储的是表的行高,和表的列宽,以及表中数据的个数
row | cow | val |
---|---|---|
10 | 14 | 6 |
1 | 1 | 1 |
3 | 6 | 1 |
5 | 3 | 5 |
6 | 6 | 2 |
8 | 12 | 1 |
9 | 5 | 1 |
通过这样一张表,我们就记录下了原始表中所有数据的位置、个数,以及原数组的大小,我们可以通过这个数组达到与上面原数组一样的存储一样多效果,而且这个数组的大小为37,相比加大数组的1014的大小节省了大量的空间,这样的数组我们就称其为稀疏数组。
2.代码实现
了解了稀疏数组的概念之后,我们要试着实现这个稀疏数组。
原数组转稀疏数组思路:
1.遍历原始的数组,获取有效数据的个数sum;
2.根据sum就可以确定稀疏数组的行数,即int sparsearray[sum+1][3];
3.将二维数组的有效数据的信息存入到稀疏数组
稀疏数组转原数组思路:
1.先读取稀疏数组第一行的数据,获取原数组的大小;
2.在逐行读取稀疏数组,获取行列数据,将值存储到对应的原数组中;
根据分析的思路,我们来逐步实现:
1、首先需要创建一个原数组,并对数组进行部分赋值
//定义原数组:
int array[][] = new int[10][14];
array[1][1] = 1;
array[2][2] = 9;
array[3][6] = 1;
array[5][3] = 5;
array[6][6] = 2;
array[8][12] = 1;
array[9][5] = 1;
2、然后,我们需要遍历以下这个数组,看看数组中的有效数据的个数
(因为根据有效数据的个数,我们能确定稀疏数组的大小)
//统计数组中有效数据的个数
int sum = 0;
System.out.println("============原数组============");
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j]!=0){
sum++;
}
System.out.print(array[i][j]+" ");
}
System.out.println();
}
可以看到,数据已经成功存入数组。
3、确定稀疏数组的大小(稀疏数组的行数=有效数据的个数+1,列数始终为3) ,稀疏数组的第一行用来存储原数组的大小和有效数据的个数。
//创建稀疏数组
int sparsearray[][] = new int [sum+1][3];
//给稀疏数组的第一行赋值,第一行存储原始数组的大小,以及值的个数
sparsearray[0][0] = array.length;
sparsearray[0][1] = array[0].length;
sparsearray[0][2] = sum;
4、我们开始遍历原数组,将原数组中有效数据的信息(行,列,值)存储到稀疏数组中。
//记录是第几个非0数据,没记录一个数据的信息就+1
int count = 0;
//遍历原数组,将非0值存放的稀疏数组中
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j]!=0){
count ++; //记录原数组中第几个非0数据
sparsearray[count][0] = i; //记录数组的行
sparsearray[count][1] = j; //记录数据的列
sparsearray[count][2] = array[i][j]; //记录数据的值
}
}
}
5、遍历稀疏数组,查看值的存放是否正确
//输出稀疏数组的形式
System.out.println("============稀疏数组============");
for (int i = 0; i < sparsearray.length; i++) {
for (int j = 0; j < sparsearray[i].length; j++) {
System.out.print(sparsearray[i][j]+"\t");
}
System.out.println();
}
可以看到,每个有效数据的信息都存储到了稀疏数组中
6、将稀疏数组转化为原数组
//将稀疏数组再转换为原数组
int array_[][] = new int[sparsearray[0][0]][sparsearray[0][1]]; //根据稀疏数组确定原数组的大小
for (int i = 1; i < sparsearray.length; i++) {
array_[sparsearray[i][0]][sparsearray[i][1]] = sparsearray[i][2];
}
7、输出原数组
//输出原数组
System.out.println("============稀疏数组转换后的原数组============");
for (int i = 0; i < array_.length; i++) {
for (int j = 0; j < array_[i].length; j++) {
System.out.print(array_[i][j]+" ");
}
System.out.println();
}
可以看到原数组也被完整的恢复。
通过这样一个例子可以看出,如果原数组特别大,但是利用率不高的时候,我们可以将其转换为稀疏数组,在这个例子中,原数组的大小为10*14,而稀疏数组的大小为8*3,两者所占的空间相差很大,而效果确一样。
但是不代表着稀疏数组适用于所有情况,我们通过例子可以得出另外一个结论,原数组中每增加一条数据,稀疏数组就需要增加一行数据来保存其信息,因此原数组每增加一个数据,稀疏数组就需要多耗费3倍原数组中新增数组所占的空间,所以当一个数组的空间利用率十分高时,我们也没有必要采用稀疏数组来存放数据。