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

目录

 

前言

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实战

把数组循环输出

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

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

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

 /**
     * 把二维数组转换成稀释数组
     * @param lists
     * @return
     */
    public static int[][] getSparseArray(int[][] lists){
            if(lists.length < 0){
                System.out.println("二维数组的长度不能为空");
                return null;
            }
            //第一步:求出sum
            AtomicInteger sum = new AtomicInteger();//记录有多少个非0的有效数据
            //得到稀疏数组
            Arrays.stream(lists).forEach(i -> {
                Arrays.stream(i).filter(o -> o!=0).forEach(n -> sum.getAndIncrement());
            });
            //第二步:创建稀疏数组
            int sparses [][] = new int [sum.get()+1][3];
            //完成稀疏数组第一列
            sparses[0][0] = lists.length; //行数
            sparses[0][1] = lists[0].length;  //列数
            sparses[0][2] = sum.get();
            int count = 0;
            for(int x=0;x<sparses[0][0];x++){
                for(int y = 0;y<sparses[0][1];y++){
                    if(lists[x][y] != 0) {
                        count+=1;
                        sparses[count][0] = x;
                        sparses[count][1] = y;
                        sparses[count][2] = lists[x][y];
                    }
                }
            }
            return sparses;
        };

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

把稀疏数据保存为文件

 /**
     * 把稀疏数据保存为文件
     * @param sparses
     * @param path
     */
    public static void sparseToFile(int[][] sparses, String path){
        FileWriter fileWriter = null;
        try {
            File file = new File(path);
            if (file.exists()) {  //存在
                file.delete();  //则删除
            }
            //目录不存在 则创建
            if (!file.getParentFile().exists()) {
                boolean mkdir = file.getParentFile().mkdirs();
                if (!mkdir) {
                    throw new RuntimeException("创建目标文件所在目录失败!");
                }
            }
            file.createNewFile();

            fileWriter = new FileWriter(path);
            for (int[] row : sparses) {
                for (int item : row) {
                    fileWriter.write(item+"\t");
                }
                //\r\n即为换行
                fileWriter.write("\r\n");
            }
            // 把缓存区内容压入文件
            fileWriter.flush();
            System.out.println("稀疏数据保存文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

把文件转为稀疏数组

/**
     * 把文件转为稀疏数组
     * @param path
     * @return
     */
    public static  int[][] fileToSparse(String path){
        File file = new File(path);
        if(!file.exists()){
            System.out.println("文件转稀疏数据失败,文件不能为空!");
            return null;
        }
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new FileReader(file));

            String line = null;
            //缓存文件里面的值,再解析处理
            StringBuilder  sb = new StringBuilder ();
            int count = 0;
            while((line = bufferedReader.readLine())!=null){
                //System.out.println("行:"+line);
                sb.append(line+"\r\n");
                count+=1;
            }
            //解析sb数据
            int sparses[][]=new int[count][3];
            String[] splits = sb.toString().split("\r\n");
            //第一行记录的是 二维数据的行和列,有效数据长度,不为有效数据
           for (int i = 0; i < splits.length; i++) {
                    String[] temp = splits[i].split("\t");
                    for(int j=0;j<temp.length;j++){
                        sparses[i][j] = Integer.parseInt(temp[j]);
                    }
            }
           return sparses;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(bufferedReader!=null){
                try {
                    bufferedReader.close();
                    bufferedReader = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

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

把稀疏数组转为二维数组

/**
     * 把稀疏数组转为二维数组
     * @param fileToSparse
     * @return
     */
    public static int[][] sparseToArray(int[][] fileToSparse){
        int[][] twoLists = new int[fileToSparse[0][0]][fileToSparse[0][1]];
        for (int i = 1;i<fileToSparse.length;i++) {
            twoLists[fileToSparse[i][0]][fileToSparse[i][1]] = fileToSparse[i][2];
        }
        return twoLists;
    }

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

github源码

博主,java小白,有问题欢迎在下方留言。大家互相交流,互相进步!
每周更新,分享各个技术点面试、结构和算法、源码分析等干货

发布了268 篇原创文章 · 获赞 436 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_16855077/article/details/104168799