飞道的博客

【算法学习】 五 为什么要使用稀疏数组?

543人阅读  评论(0)

前言

社长,一个爱学习,爱分享的程序猿,始终相信,付出总会有回报的。知识改变命运,学习成就未来。爱拼才会赢!
程序猿学社的GitHub,已整理成相关技术专刊,欢迎Star:
https://github.com/ITfqyd/cxyxs

目录

前言

1. 简介

稀疏数据的记录方式:

2. 场景

3. 通过图更好的理解分析

实现逻辑

二维数组存储

稀疏数组存储方式

二维数组转稀疏数组的思路

稀疏数组转二维数组的思路

4实战

把数组循环输出

把二维数组转换成稀释数组

把稀疏数据保存为文件

把文件转为稀疏数组

把稀疏数组转为二维数组

1. 简介

稀疏数组(Sparse array) ,所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。

稀疏数据的记录方式:

  1. 记录数组一共有几行几列,有值的地方有多少处
  2. 把有值位置的行、列对应的值都记录起来,只记录有下子的位置,从而节约内存空间。

2. 场景

适用于围棋,扫雷等等

 


五子棋,扫雷,跟我一个年代的社友,应该都经历过,以前小时候上信息课,教室是没有网络的,只能玩这些不用联网的小游戏。
社长发现,现在很多的游戏,都多了一个继续游戏的按钮。也就是说,就算你退出窗口后,还能从上一次的位置开始。他是怎么还能从上一次的位置开始的?
社长,就把这个问题,抛到朋友群里面。
隔壁小王说:社长,这个问题,很简单呀,就是通过二维数据存储。
大佬H说:二维数据效率太浪费空间了,假设白子为1,黑子为2,没有下的地方为0,我们可以发现很多的值都为0,建议使用稀疏数组保存,再生成文件,持久化到磁盘中。

 

 

3. 通过图更好的理解分析

实现逻辑

 


 


假设9*9列棋盘,白子为1,黑子为2,没有下子位置为0

 

二维数组存储

 

 

稀疏数组存储方式

 


由99变成53,是不是节约了空间。

 

二维数组转稀疏数组的思路

  1. 遍历整合二维数组,得到有效棋子有多少个。假设为count
  2. 创建稀疏数组 int[count+1] [3],例如9*9的棋盘上,有效棋子4个,但是数组的长度为5,第一条记录,记录,这个二维数据,有多少行,多少列,有效的数据为多少。
  3. 二维数组中不为0的数,存入稀疏数组中。

稀疏数组转二维数组的思路

  1. 读取第一行的值,可以得到二维数组的行和列,创建二维数组。
  2. 继续读取后面的值,根据行列值对应的映射关系,填充到二维数组中。

4实战

把数组循环输出


  
  1.    /**
  2.      * 把数组循环输出
  3.      */
  4.          public static  void printArray(int[][] lists){
  5.              //使用jdk1.8新特性 ---第一种方法实现
  6.             Arrays.stream(lists).forEach(i -> {
  7.                 Arrays.stream(i).forEach(n -> System. out.printf( "%d\t", n));
  8.                 System. out.println();
  9.             });
  10.              //或者使用for增强方式  ---第二种方法实现
  11.              /*for (int[] row : lists) {
  12.                 for (int item : row) {
  13.                     System.out.printf("%d\t",  item);
  14.                 }
  15.                 System.out.println();
  16.             }*/
  17.         }

注意:代码中使用jdk1.8的新特性,有两种方式可以实现

把二维数组转换成稀释数组


  
  1.   /**
  2.      * 把二维数组转换成稀释数组
  3.      * @param lists
  4.      * @return
  5.      */
  6.      public  static  int[][] getSparseArray( int[][] lists){
  7.              if(lists.length <  0){
  8.                 System. out.println( "二维数组的长度不能为空");
  9.                  return  null;
  10.             }
  11.              //第一步:求出sum
  12.             AtomicInteger sum =  new AtomicInteger(); //记录有多少个非0的有效数据
  13.              //得到稀疏数组
  14.             Arrays.stream(lists).forEach(i -> {
  15.                 Arrays.stream(i).filter(o -> o!= 0).forEach(n -> sum.getAndIncrement());
  16.             });
  17.              //第二步:创建稀疏数组
  18.              int sparses [][] =  new  int [sum. get()+ 1][ 3];
  19.              //完成稀疏数组第一列
  20.             sparses[ 0][ 0] = lists.length;  //行数
  21.             sparses[ 0][ 1] = lists[ 0].length;   //列数
  22.             sparses[ 0][ 2] = sum. get();
  23.              int count =  0;
  24.              for( int x= 0;x<sparses[ 0][ 0];x++){
  25.                  for( int y =  0;y<sparses[ 0][ 1];y++){
  26.                      if(lists[x][y] !=  0) {
  27.                         count+= 1;
  28.                         sparses[count][ 0] = x;
  29.                         sparses[count][ 1] = y;
  30.                         sparses[count][ 2] = lists[x][y];
  31.                     }
  32.                 }
  33.             }
  34.              return sparses;
  35.         };

AtomicInteger标识原子性,也就是多线程过程中i++,高并发,假设1000个线程,每个都调用一次i++,最终的结果应该是1000,但是,不好意思,最终的结果可能会小于1000。可以把该字段设置为AtomicInteger,最终的结果一定是1000.
第一步:求出sum
第二步:创建稀疏数组
第三步:把二维数组的行、列、有效数据,转为稀疏数组第一行的值。
第三步,依次把二维数据对应的行、列、值映射到稀疏数组中。

把稀疏数据保存为文件


  
  1.   /**
  2.      * 把稀疏数据保存为文件
  3.      * @param sparses
  4.      * @param path
  5.      */
  6.      public static void sparseToFile(int[][] sparses, String path){
  7.         FileWriter fileWriter =  null;
  8.          try {
  9.             File file =  new File(path);
  10.              if (file.exists()) {   //存在
  11.                 file.delete();   //则删除
  12.             }
  13.              //目录不存在 则创建
  14.              if (!file.getParentFile().exists()) {
  15.                  boolean mkdir = file.getParentFile().mkdirs();
  16.                  if (!mkdir) {
  17.                      throw  new RuntimeException( "创建目标文件所在目录失败!");
  18.                 }
  19.             }
  20.             file.createNewFile();
  21.             fileWriter =  new FileWriter(path);
  22.              for ( int[] row : sparses) {
  23.                  for ( int item : row) {
  24.                     fileWriter.write(item+ "\t");
  25.                 }
  26.                  //\r\n即为换行
  27.                 fileWriter.write( "\r\n");
  28.             }
  29.              // 把缓存区内容压入文件
  30.             fileWriter.flush();
  31.             System.out.println( "稀疏数据保存文件成功!");
  32.         }  catch (IOException e) {
  33.             e.printStackTrace();
  34.         } finally {
  35.              if(fileWriter!= null){
  36.                  try {
  37.                     fileWriter.close();
  38.                 }  catch (IOException e) {
  39.                     e.printStackTrace();
  40.                 }
  41.             }
  42.         }
  43.     }

第一步:文件存在,则删除,目录不存在,则创建
第二步:依次把稀疏数组的值按9\t9\t4\n的格式插入。

把文件转为稀疏数组


  
  1. /**
  2.      * 把文件转为稀疏数组
  3.      * @param path
  4.      * @return
  5.      */
  6.      public  static   int[][] fileToSparse(String path){
  7.         File file =  new File(path);
  8.          if(!file.exists()){
  9.             System.out.println( "文件转稀疏数据失败,文件不能为空!");
  10.              return  null;
  11.         }
  12.         BufferedReader bufferedReader =  null;
  13.          try {
  14.             bufferedReader =  new BufferedReader( new FileReader(file));
  15.             String line =  null;
  16.              //缓存文件里面的值,再解析处理
  17.             StringBuilder  sb =  new StringBuilder ();
  18.              int count =  0;
  19.              while((line = bufferedReader.readLine())!= null){
  20.                  //System.out.println("行:"+line);
  21.                 sb.append(line+ "\r\n");
  22.                 count+= 1;
  23.             }
  24.              //解析sb数据
  25.              int sparses[][]= new  int[count][ 3];
  26.             String[] splits = sb.toString().split( "\r\n");
  27.              //第一行记录的是 二维数据的行和列,有效数据长度,不为有效数据
  28.             for ( int i =  0; i < splits.length; i++) {
  29.                     String[] temp = splits[i].split( "\t");
  30.                      for( int j= 0;j<temp.length;j++){
  31.                         sparses[i][j] = Integer.parseInt(temp[j]);
  32.                     }
  33.             }
  34.             return sparses;
  35.         }  catch (Exception e) {
  36.             e.printStackTrace();
  37.         } finally {
  38.              if(bufferedReader!= null){
  39.                  try {
  40.                     bufferedReader.close();
  41.                     bufferedReader =  null;
  42.                 }  catch (IOException e) {
  43.                     e.printStackTrace();
  44.                 }
  45.             }
  46.         }
  47.          return  null;
  48.     }

第一步:判断文件是否存在,不存在则提示
第二步:读取文件的值,先缓存到StringBuilder,因为稀疏数组的长度,只能通过读取所有的行才能获取。
第三步:创建稀疏数组,稀疏数组的[行数+1][3] 列固定为3
第三步:依次缓存字符串按9\t\9\t4\n解析,把对应的写入稀疏数组中。

把稀疏数组转为二维数组


  
  1. /**
  2.      * 把稀疏数组转为二维数组
  3.      * @param fileToSparse
  4.      * @return
  5.      */
  6.      public  static  int[][] sparseToArray( int[][] fileToSparse){
  7.          int[][] twoLists =  new  int[fileToSparse[ 0][ 0]][fileToSparse[ 0][ 1]];
  8.          for ( int i =  1;i<fileToSparse.length;i++) {
  9.             twoLists[fileToSparse[i][ 0]][fileToSparse[i][ 1]] = fileToSparse[i][ 2];
  10.         }
  11.          return twoLists;
  12.     }

第一步:稀疏数组的[0][0]标识二维数组行 [0][1]二维数组列
第二步:创建二维数组
第三步:循环稀疏数组,依次稀疏数组放入二维数组中。

github源码

后记

程序猿学社的GitHub,欢迎Star:
https://github.com/ITfqyd/cxyxs
觉得有用,可以点赞,关注,评论,留言四连发。

 


转载:https://blog.csdn.net/qq_16855077/article/details/104168799
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场