⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现,有时候有C/C++代码。
⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁
目录
- 1、简介
- 2、简单例子
- 3、优缺点
- 4、应用场景
- 5、代码实现
- 5.1、简述步骤
- 5.2、Java实现
- 5.2.1、无IO流
- 5.2.2、结合IO流
1、简介
稀疏数组是一种压缩数据的方式,它在处理大规模二维数组时,可以用较小的存储空间来表示大部分元素都为默认值或者重复值的数组。在这种情况下,使用稀疏数组可以显著减少存储空间的占用。
稀疏数组的一般形式是一个三元组 [i, j, value]
,其中 i
和 j
表示数组中某个元素的行和列,value
表示该元素的值。通常情况下,我们会用一个二维数组来表示原始数组,并将稀疏数组存储在一个一维数组中。
2、简单例子
比如有一个原始数组是10 X 10的,其中数据不为零的索引为:[1,2],[2,3],[3,4],其余数据为0,
如下所示:
创建一个稀疏数组,首行记录原始数组的行数、列数、非零的有效值个数,从第二行开始,记录有效值的行索引、列索引、有效值,
如下:
3、优缺点
使用稀疏数组的优点:
- 空间利用率高:稀疏数组只存储非默认值的元素信息,可以大大减少存储空间的占用。
- 方便进行压缩和解压缩:稀疏数组可以方便地进行压缩和解压缩操作,对于大型稀疏矩阵的存储和传输非常有用。
- 可以提高运算效率:稀疏数组在进行运算时,可以只对非默认值元素进行处理,避免对所有元素进行无效操作,从而提高运算效率。
使用稀疏数组的缺点:
- 降低了数组的访问速度:由于稀疏数组中非默认值元素的位置信息需要额外存储,访问这些元素的速度相对较慢,特别是在大规模稀疏矩阵中。
- 需要进行额外的处理:使用稀疏数组需要额外处理元素的位置信息,这会增加代码的复杂性和维护成本。
- 不适用于密集矩阵:对于密集矩阵,使用稀疏数组存储反而会增加存储空间的占用。
综上所述,稀疏数组适用于大部分元素都为默认值或重复值的情况,可以显著减少存储空间的占用,但同时也需要特别注意其访问速度和代码复杂性等问题。
4、应用场景
稀疏数组主要应用于以下场景:
- 图像处理:在处理图像时,由于图像中大多数像素值是相同的或者为默认值,因此使用稀疏数组可以大大减少存储空间。
- 文本处理:在文本处理中,使用稀疏数组可以节省存储空间,特别是对于文本中大量重复的字符或单词。
- 矩阵计算:在矩阵计算中,由于大多数矩阵元素都是零,因此使用稀疏数组可以显著提高计算效率。
- 数据库系统:在数据库系统中,由于大多数数据都是默认值或者重复数据,因此使用稀疏数组可以节省存储空间和提高查询效率。
- 网络传输:在网络传输中,使用稀疏数组可以减少传输数据的大小,从而提高传输速度。
总的来说,稀疏数组适用于大部分元素都为默认值或重复值的情况,可以显著减少存储空间的占用,并且在某些场景下可以提高计算效率和传输速度。
5、代码实现
5.1、简述步骤
二维数组 转 稀疏数组的思路:
- 遍历 原始的二维数组,得到有效数据的个数
- 根据 有效数据的个数 就可以创建 稀疏数组
- 将二维数组的有效数据数据存入到 稀疏数组
稀疏数组转原始的二维数组的思路
- 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组
- 在读取稀疏数组后几行的数据,并赋给 原始的二维数组 即可.
下面用代码详细实现
5.2、Java实现
5.2.1、无IO流
这里重点先了解如何实现稀疏数组和稀疏数组向原始数组的转换:
①创建原始数组
②创建稀疏数组
/**
稀疏数组
*/
public static int[][] arrayToSparseArr(int[][] array) {
//1、获取有效数据,即不为零的数据
int count = 0;
for (int[] row : array) {
for (int data : row) {
if (data != 0) {
count++;
}
}
}
//创建稀疏数组
int[][] sparseArray = new int[count + 1][3];
//设置稀疏数组首行的值
//row
sparseArray[0][0] = 10;
//row_data
sparseArray[0][1] = 10;
//value
sparseArray[0][2] = count;
//遍历存入有效数据,定义一个索引值
int index = 1;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j] != 0) {
sparseArray[index][0] = i;
sparseArray[index][1] = j;
sparseArray[index][2] = array[i][j];
index++;
}
}
}
for (int[] row : sparseArray) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
return sparseArray;
}
③稀疏数组转原始数组
完整代码:
package sparseArray;
/**
* @author 逐梦苍穹
* @date 2023/4/12 23:14
*/
public class SparseArray {
public static void main(String[] args) {
//创建原始数组
int[][] array = new int[10][10];
array[1][2] = 1;
array[2][3] = 2;
array[3][4] = 3;
System.out.println("原始数组:");
for (int[] row : array) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//稀疏数组
System.out.println("稀疏数组:");
int[][] sparseArray = arrayToSparseArr(array);
System.out.println("稀疏数组->原始数组:");
//回到原始数组
for (int[] row : sparseArrToArray(sparseArray)) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
}
public static int[][] arrayToSparseArr(int[][] array) {
//稀疏数组
//1、获取有效数据,即不为零的数据
int count = 0;
for (int[] row : array) {
for (int data : row) {
if (data != 0) {
count++;
}
}
}
//创建稀疏数组
int[][] sparseArray = new int[count + 1][3];
//设置稀疏数组首行的值
//row
sparseArray[0][0] = 10;
//row_data
sparseArray[0][1] = 10;
//value
sparseArray[0][2] = count;
//遍历存入有效数据,定义一个索引值
int index = 1;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j] != 0) {
sparseArray[index][0] = i;
sparseArray[index][1] = j;
sparseArray[index][2] = array[i][j];
index++;
}
}
}
for (int[] row : sparseArray) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
return sparseArray;
}
public static int[][] sparseArrToArray(int[][] sparseArr) {
int[][] array = new int[sparseArr[0][0]][sparseArr[0][1]];
for (int i = 1; i < sparseArr.length; i++) {
array[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
return array;
}
}
5.2.2、结合IO流
先看一下代码整体思路总览:
下面重点说IO读写:
①IO输出流,把稀疏数组输出到文件。
思路是:
- 先创建文件对象,判断该对象是否存在
- 获取字符缓冲输出流,在流里面传入转换流,在转换流里面传入文件输出流,传入参数为文件路径和指定字符集编码。(如果不需要指定字符编码,直接获取字符缓冲流即可,像这样:
) - 遍历内存中的稀疏数组,调用输出流的write方法往文件中写入数据。
输出流部分的代码如下:
②IO输入流,把文件中的稀疏数组数据输入到内存。
思路是:
- 获取字符缓冲输入流,在流里面传入转换流,在转换流里面传入文件输入流,传入参数为文件路径和指定字符集编码。(如果不需要指定字符编码,直接获取字符缓冲流即可,像这样:
) - 遍历内存中的稀疏数组,调用输入流的readLine方法按行读取文件中的数据
输入流部分的代码如下:
结合IO流的完整代码如下:
package sparseArray;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @author 逐梦苍穹
* @date 2023/4/13 10:00
*/
public class SparseArrayIo {
/**
* main方法中创建原始数组和调用各种静态方法
*/
public static void main(String[] args) throws Exception {
//创建原始数组
int[][] array = new int[10][10];
array[1][2] = 1;
array[2][3] = 2;
array[3][4] = 3;
System.out.println("原始数组:");
for (int[] row : array) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//稀疏数组
System.out.println("稀疏数组:");
int[][] sparseArray = arrayToSparseArr(array);
System.out.println("稀疏数组->原始数组:");
//回到原始数组
for (int[] row : sparseArrToArray(sparseArray)) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//稀疏数组写入文件
String filePath = "src/sparseArray/sparseArray.txt";
boolean flag = writeSparseArrayToFile(sparseArray, filePath);
//稀疏数组数据文件写入内存
if (flag) {
//从文件写入内存
System.out.println("从文件写入内存:");
writeFileToRam(filePath);
}
}
/**
* 稀疏数组
*/
public static int[][] arrayToSparseArr(int[][] array) {
//1、获取有效数据,即不为零的数据
int count = 0;
for (int[] row : array) {
for (int data : row) {
if (data != 0) {
count++;
}
}
}
//创建稀疏数组
int[][] sparseArray = new int[count + 1][3];
//设置稀疏数组首行的值
//row
sparseArray[0][0] = 10;
//row_data
sparseArray[0][1] = 10;
//value
sparseArray[0][2] = count;
//遍历存入有效数据,定义一个索引值
int index = 1;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (array[i][j] != 0) {
sparseArray[index][0] = i;
sparseArray[index][1] = j;
sparseArray[index][2] = array[i][j];
index++;
}
}
}
for (int[] row : sparseArray) {
for (int data : row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
return sparseArray;
}
/**
* 稀疏数组转原始数组
*/
public static int[][] sparseArrToArray(int[][] sparseArr) {
int[][] array = new int[sparseArr[0][0]][sparseArr[0][1]];
for (int i = 1; i < sparseArr.length; i++) {
array[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
}
return array;
}
/**
* 稀疏数组写入磁盘
*/
public static boolean writeSparseArrayToFile(int[][] sparseArray, String filePath) throws Exception {
boolean flag = false;
//得到了稀疏数组,此时要把它存入磁盘
File file = new File(filePath);
if (!file.exists()) {
flag = file.createNewFile();
}
if (flag) {
try (
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath), StandardCharsets.UTF_8))
) {
for (int i = 0; i < sparseArray.length; i++) {
for (int j = 0; j < sparseArray[i].length; j++) {
bw.write(sparseArray[i][j] + "\t");
}
if (i == sparseArray.length - 1) {
break;
} else {
bw.write("\n");
}
}
} catch (Exception e) {
System.out.println(e);
return false;
}
return true;
} else {
return false;
}
}
/**
* 磁盘文件写入内存
*/
public static void writeFileToRam(String filePath) throws Exception {
try (
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))
) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}