java数据结构之从稀疏矩阵到稀疏数组

news/2024/6/15 6:26:07 标签: 数据结构, 稀疏数组, 稀疏矩阵, java

先看一下定义
稀疏矩阵:在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵;与之相反,若非0元素数目占大多数时,则称该矩阵为稠密矩阵。定义非零元素的总数比上矩阵所有元素的总数为矩阵的稠密度。
这是线性代数中的内容,对应到咱们写代码上,其实就是一个二维数组,但如果使用二维数组存储这种类型的数据,很明显发现占用了很多无效的存储空间,反而存储了较少有效的数据。于是乎,使用稀疏数组存储这些内容的方法实现了,可以大大节省存储空间。
先看一个简单的例子,下面再分享代码:
比如有这样一个二维数组:
0	0	0	00	0	1	00	0	0	20	0	0	0
存储了16个值,但真正有用的就两个值,那么有没有办法只把有效的值存起来,其他冗余的就给一个默认值,这种存储方式就是稀疏数组
先看存起来是啥样子( 行数:有效数据个数 + 1,列数:3列,其余值默认为0)
在这里插入图片描述
不得不佩服人家的思想,这境界!!!
下面用代码来实现一个现实的案例:
我在下围棋时,下到了这样一个局面,临时有事,我想把它保存下来,下次有空的时候再下,这样怎么保存呢。
在这里插入图片描述
两种方法,一种用二维数组保存,一种用稀疏数组保存,两种都实现一下,最后对比一下存储的大小。

java">import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class 稀疏数组 {

	public static void main(String[] args) throws IOException {
		// 创建一个原始的二维数组 13 * 13
		// 0: 表示没有棋子, 1 表示黑子 2 表示白子
		int chessArr1[][] = new int[13][13];
		chessArr1[2][5] = 1;
		chessArr1[3][10] = 1;
		chessArr1[4][9] = 1;
		chessArr1[9][3] = 1;
		
		chessArr1[2][8] = 2;
		chessArr1[3][3] = 2;
		chessArr1[7][2] = 2;
		chessArr1[10][5] = 2;
		// 输出原始的二维数组
		System.out.println("原始的二维数组--------------->");
		//写到文件里
		FileWriter osw = new FileWriter("D:\\360MoveData\\Users\\Administrator\\Desktop\\原始二维数组.txt");
		BufferedWriter   bw = new   BufferedWriter(osw);   

		for (int[] row : chessArr1) {
			for (int data : row) {
				bw.write(data + "	");
				System.out.printf("%d\t", data);
			}
			bw.newLine();
			System.out.println();
		}
		//关闭流
		bw.flush();
		bw.close();
		osw.close();
		
		// 将二维数组 转 稀疏数组的思
		// 1. 先遍历二维数组 得到非0数据的个数
		int sum = 0;
		for (int i = 0; i < 13; i++) {
			for (int j = 0; j < 13; j++) {
				if (chessArr1[i][j] != 0) {
					sum++;
				}
			}
		}

		// 2. 创建对应的稀疏数组
		int sparseArr[][] = new int[sum + 1][3];
		// 给稀疏数组赋值
		sparseArr[0][0] = 13;
		sparseArr[0][1] = 13;
		sparseArr[0][2] = sum;

		// 遍历二维数组,将非0的值存放到 sparseArr中
		int count = 0; //count 用于记录是第几个非0数据
		for (int i = 0; i < 13; i++) {
			for (int j = 0; j < 13; j++) {
				if (chessArr1[i][j] != 0) {
					count++;
					sparseArr[count][0] = i;
					sparseArr[count][1] = j;
					sparseArr[count][2] = chessArr1[i][j];
				}
			}
		}
		// 输出稀疏数组的形式
		System.out.println("-------------------------------------------------------");
		System.out.println("生成的稀疏数组---------------------->");
		
		FileWriter fileWriter = new FileWriter("D:\\360MoveData\\Users\\Administrator\\Desktop\\稀疏数组.txt");
		BufferedWriter   bufferedWriter = new   BufferedWriter(fileWriter);   
		
		for (int i = 0; i < sparseArr.length; i++) {
			bufferedWriter.write(sparseArr[i][0] + "	" + sparseArr[i][1] + "    " + sparseArr[i][2]);
			bufferedWriter.newLine();
			System.out.printf("%d\t%d\t%d\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
		}
		bufferedWriter.close();
		fileWriter.close();
		
		
		//将稀疏数组 ----> 恢复成原始的二维数组
		/*
		 *  1. 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的  chessArr2 = int [11][11]
			2. 在读取稀疏数组后几行的数据,并赋给 原始的二维数组 即可.
		 */

		//1. 先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组

		int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];

		//2. 在读取稀疏数组后几行的数据(从第二行开始),并赋给 原始的二维数组 即可

		for(int i = 1; i < sparseArr.length; i++) {
			chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
		}

		// 输出恢复后的二维数组
		System.out.println();
		System.out.println("恢复后的二维数组");

		for (int[] row : chessArr2) {
			for (int data : row) {
				System.out.printf("%d\t", data);
			}
			System.out.println();
		}
		

	}

}

代码运行一下,看一下控制台:
在这里插入图片描述
再看一下存储的文件:
在这里插入图片描述
在这里插入图片描述
节省空间的效果的还是很明显的,也许有人会说占用的空间一样大,这是因为文件本身就太小,和磁盘存储的簇大小有关,最小就是4096个字节,你如果使用海量数据试一下,就有明显区别了
那是不是永远都是使用稀疏数组节省空间呢,显然不是的,这就和最上面提到的稠密度有关了,
通常认为矩阵中非零元素的总数比上矩阵所有元素总数的值小于等于0.05时,则称该矩阵为稀疏矩阵(sparse matrix),该比值称为这个矩阵的稠密度,那么这时候就相对应使用稀疏数组为较优方案


http://www.niftyadmin.cn/n/1032412.html

相关文章

VMware中ubuntu16.04 NAT模式设置

目录 前提&#xff1a;网络适配器设置成NAT模式&#xff0c;如下图 1.检查实体机的VMnet8网卡设置和虚拟机网卡设置是否保持一致&#xff1a; 1.1 VMware中“编辑”-“虚拟网络编辑器”-“更改设置”&#xff1a; 1.2查询子网是什么 1.3查询网关是什么 2设置实体机VMnet8…

操作list时报java.util.ConcurrentModificationException

这是一个大坑&#xff0c;隐藏的比较隐蔽&#xff0c;有时候还不容易发现&#xff0c;先看我遇到的具体问题&#xff1a; 我修改一位新手同事的一段代码&#xff0c;写的是对一个list做均匀切割&#xff0c;处理&#xff0c;每次搞到中间就报错&#xff0c;提示&#xff1a; ja…

winform做的单机登录界面和账号注册界面

代码&#xff1a; public partial class LoginForm : DevExpress.XtraEditors.XtraForm{public LoginForm(){Logger.RecordInfo("初始化登录页面");InitializeComponent();}//用户登录&#xff0c;验证账号密码private void simpleButton1_Click(object sender, Eve…

VSCode远程连接ubuntu服务器

1.打开VSCode&#xff0c;安装插件 安装插件&#xff0c;汉化&#xff0c;方法如下。重启之后界面就都是中文了。因为我之前弄过了&#xff0c;如果你的已经是中文了&#xff0c;可以省去这一步。 安装remote development插件&#xff0c;如下图。用于远程连接服务器。 安装好…

C#项目中直接使用cmd调用jar包和Python脚本

调用jar包&#xff0c;首先你得先开发一个jar包&#xff0c;可以自己通过命令&#xff1a; java -jar xxxxx.jar param1 正常调用。调用代码&#xff1a; private void button3_Click(object sender, EventArgs e){Process p new Process();//设置要启动的应用程序…

1.2 XShell清空屏幕内容快捷键

ctrlL&#xff08;大小写均可&#xff09; 参考&#xff1a;牛客网 C高薪求职项目《Linux高并发服务器开发》1.2 GCC(1) 专属优惠链接&#xff1a; https://www.nowcoder.com/courses/cover/live/504?couponAvTPnSG 通过上方链接购买&#xff0c;立减150元哦&#xff01;&a…

win7系统调用tts的语音朗读功能

windows的tts组件&#xff0c;正版系统或者win10系统是可以直接调用成功的&#xff0c;但win7有的是阉割版&#xff0c;有的不支持&#xff0c;调用的时候各种异常&#xff0c;网上的人各种抄袭&#xff0c;不知道所以然&#xff0c;还爱瞎bb&#xff0c;我就费了好大劲才完全搞…

bootstrap中固定表头,内容滚动,表头不动

就是这个样子&#xff1a; 不论滚动条再怎么滚&#xff0c;表头始终不动。 处理起来很简单&#xff0c;但网上总有人瞎bb&#xff0c;我也是费了好大劲&#xff0c;终于好了。 <div class"panel-footer" style"overflow: auto;"><table id"…