java实现稀疏数组

简介: 稀疏数组是一种为了节约存储空间而产生的数据结构,本质上稀疏数组就是一个普通的二维数组。其实在真实的应用中,稀疏数组的用武之地很少,起码笔者工作了几年,是没有发现稀疏数组的用武之地的(感觉游戏领域可能会用到),但是作为数据结构的一种,我们学习他还是可以得到一些启发的,比如使用时间换空间的思想,反过来使用空间换时间不也是可以的吗。所以学习他不一定非要用,但是肯定会对自己的思维有帮助。

前言:


稀疏数组是一种为了节约存储空间而产生的数据结构,本质上稀疏数组就是一个普通的二维数组。其实在真实的应用中,稀疏数组的用武之地很少,起码笔者工作了几年,是没有发现稀疏数组的用武之地的(感觉游戏领域可能会用到),但是作为数据结构的一种,我们学习他还是可以得到一些启发的,比如使用时间换空间的思想,反过来使用空间换时间不也是可以的吗。所以学习他不一定非要用,但是肯定会对自己的思维有帮助。


一、稀疏数组介绍



1.什么是稀疏数组


在多维数组中,若是存在大量重复值,那么我们可以选择,将重复值排除掉,只保留那些有意义的数据,将这些有意义信息存入到一个二维数组中,这个二维数据就叫做稀疏数组,在原始数组是二维数组时,稀疏数组就是一个普通的n行3列的二维数组,下面也将以此场景来聊聊稀疏数组。


2.稀疏数组的实现原理


稀疏数组是一个n行3列的二维数组,那为什么是3列呢?仔细分析下就可以明白,在原始的二维数组中一个有效数据包含了3个必须记录的信息,即:行数、列数、值。只要记录了这三个值,我们就可以将原来的二维数组转变为稀疏数组,同样的有了这三个值,我们还可以将稀疏数组还原到原来的二维数组,这样也就明白了稀疏数组实现的原理了,原理就是只记录原二维数组中的有效数据,记录的是有效数据的行、列、值,这三个值构成了稀疏数组的一个行,所以一行有三列。不过这些记录是可以还原数组的所有的有效数据,但是我们并不清楚原数据到底有多少行多少列啊,所以还需要记录原始数组的行、列、有效数据的个数。这三个值一般放在数组的第0行。


3.稀疏数组规律


根据以上信息我们轻易的可以发现稀疏数组是一个这样的结构:(原始数组有效数据个数+1)行,3列的二维数组。假设原有数组是x行y列有n个有效数据那我们可以得出这么一个公式:xy>(n+1)3,这样我们使用稀疏数组才会有意义(此时稀疏数组节点个数小于原二维数组),不然使用稀疏数组,既浪费了时间,也没有节约空间。所以假如要使用稀疏数组这个是必须判断的。比如有一个10行9列的二维数组,所以我们可以得出这个公式:90>(n+1)*3,可以发现n<29时也就是有效数据小于29时,我们使用稀疏数组是节约了空间的,此时使用稀疏数组才是有意义的。当然事实上可能这个临界值并不是一个好的判断节点,可能要空间节约很多时,才能弥补的上原始二维数组转化为细稀疏数组带来的性能消耗。这里只是提供一个判断思路。


二、稀疏数组的实现



这里总结下,二维数组转稀疏数组和稀疏数组转二维数组的过程。


1.将二维数组转化为稀疏数组


首先明确思路:第一步我们应该获取到原数组的行数列数,以及有效数据的个数。第二步就是遍历原二维数据将有效数据存入到稀疏数组。这就是我们的实现思路,然后跟着思路去实现代码,如下:


  //二维数组有效数据个数
    static int countValid = 0;
  /**
     * 将二维数组转变为稀疏数组sparsearray
     * 实现思路:
     *      1.首先确定二维数组中有效数据的个数,用以得到稀疏数组的行数:二维数组有效数据个数+1
     *      2.将二维数组中有效数据,存入到稀疏数组中
     *      3.返回稀疏数组
     * @param twoDimensionArray
     * @return
     */
    public static  int[][] changeTwoDimensionToSparseArray(int[][] twoDimensionArray){
        //判断数组是否有效
        if(twoDimensionArray.length>0 && twoDimensionArray[0].length>0){
            //通过一次遍历获取到所有有效数据
            Arrays.stream(twoDimensionArray).forEach(intArray ->{
                Arrays.stream(intArray).forEach(intNum ->{
                    if(intNum!=0){
                        countValid++;
                    }
                });
            });
            //创建稀疏数组,并将第一列值创建出来分别是:二维数组行数、列数、有效数据个数
            int[][] newSparseArray = new int[countValid+1][3];
            newSparseArray[0][0]=twoDimensionArray.length;
            newSparseArray[0][1]=twoDimensionArray[0].length;
            newSparseArray[0][2]=countValid;
            //定义稀疏数组行初始值
            int line = 1;
            //遍历二维数组,将有效数据传入到稀疏数组中
            for(int i=0;i<twoDimensionArray.length;i++){
                for(int j=0;j<twoDimensionArray[i].length;j++){
                    if(twoDimensionArray[i][j]!=0){
                        newSparseArray[line][0]=i;
                        newSparseArray[line][1]=j;
                        newSparseArray[line][2]=twoDimensionArray[i][j];
                        line++;
                    }
                }
            }
            return newSparseArray;
        }
        return null;
    }


2.将稀疏数组转化为二维数组


首先明确思路:第一步根据稀疏数组第一行信息创建二维数组,第二步遍历稀疏数组取出信息放入二维数组中。这样我们就可以实现将稀疏数组返显到二维数组了。


  /**
     * 稀疏数组转变为二维数组
     * 实现逻辑:
     *      1.根据稀稀数组第一行的信息,创建二维数组。
     *      2.遍历二维数组,将信息取出,放入到二维数组中。
     *      3.返回二维数组
     * @param sparseArray
     * @return
     */
    public static int[][] changeSparseArrayToTwoDimension(int[][] sparseArray){
        //判断稀疏数组是否有效
        if(sparseArray.length>0 && sparseArray[0].length>0){
            //根据二维数组创建稀疏数组
            int[][] twoDimensionArray = new int[sparseArray[0][0]][sparseArray[0][1]];
            //遍历稀疏数组,将有效数据取出,存入到二维数组
            for(int i=0;i<sparseArray.length;i++){
                //第一行非有效数据,不取
                if(i>0){
                    //二维数组有效元素的行数是,i行0列,二维数组的列数是i行1列,二维数组有效元素值是i行2列
                    twoDimensionArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
                }
            }
            return twoDimensionArray;
        }
        return null;
    }


3.简单的数组打印方法


再封装一个简单的数组打印方法,供来回测试调用。


  //打印二维数组
    public static void  printArray(int[][] thisArray){
        Arrays.stream(thisArray).forEach(intArray ->{
            Arrays.stream(intArray).forEach( intValue ->{
                System.out.print(intValue);
            });
            System.out.println();
        });
    }


4.测试结果


写一段测试代码,如下,测试下程序运行是否得到了想要的结果:


 public static void main(String[] args) {
        int[][] firstArray = new int[10][10];
        firstArray[3][4] = 1;
        firstArray[0][6] = 1;
        firstArray[2][3] = 1;
        printArray(firstArray);
        System.out.println("***********************************************************");
        //调用二维数组转稀疏数组方法
        int[][] sparseArray = changeTwoDimensionToSparseArray(firstArray);
        printArray(sparseArray);
        System.out.println("***********************************************************");
        //调用稀疏数组转二维数组方法
        int[][] twoDimensionArray = changeSparseArrayToTwoDimension(sparseArray);
        printArray(twoDimensionArray);
    }


下面展示下输出结果:


20210409200854943.gif

上图中第一部分是原始数组信息,第二部分是稀疏数组,第三部分是稀疏数组转换的二维数组,从上面的图片中我们可以看到无论是二维数组转稀疏数组,还是稀疏数组转化为二维数组,都没有任何问题。这样所有的功能也就实现了。


4.贴下完整的代码


上面代码都是分段展示的,这里贴下完整的代码。


public class TestSparseArray {
    //因为使用lamdba,需要将其定义为类变量
    //二维数组有效数据个数
    static int countValid = 0;
    public static void main(String[] args) {
        int[][] firstArray = new int[10][10];
        firstArray[3][4] = 1;
        firstArray[0][6] = 1;
        firstArray[2][3] = 1;
        printArray(firstArray);
        System.out.println("***********************************************************");
        //调用二维数组转稀疏数组方法
        int[][] sparseArray = changeTwoDimensionToSparseArray(firstArray);
        printArray(sparseArray);
        System.out.println("***********************************************************");
        //调用稀疏数组转二维数组方法
        int[][] twoDimensionArray = changeSparseArrayToTwoDimension(sparseArray);
        printArray(twoDimensionArray);
    }
    //打印二维数组
    public static void  printArray(int[][] thisArray){
        Arrays.stream(thisArray).forEach(intArray ->{
            Arrays.stream(intArray).forEach( intValue ->{
                System.out.print(intValue);
            });
            System.out.println();
        });
    }
    /**
     * 将二维数组转变为稀疏数组sparsearray
     * 实现思路:
     *      1.首先确定二维数组中有效数据的个数,用以得到稀疏数组的行数:二维数组有效数据个数+1
     *      2.将二维数组中有效数据,存入到稀疏数组中
     *      3.返回稀疏数组
     * @param twoDimensionArray
     * @return
     */
    public static  int[][] changeTwoDimensionToSparseArray(int[][] twoDimensionArray){
        //判断数组是否有效
        if(twoDimensionArray.length>0 && twoDimensionArray[0].length>0){
            //通过一次遍历获取到所有有效数据
            Arrays.stream(twoDimensionArray).forEach(intArray ->{
                Arrays.stream(intArray).forEach(intNum ->{
                    if(intNum!=0){
                        countValid++;
                    }
                });
            });
            //创建稀疏数组,并将第一列值创建出来分别是:二维数组行数、列数、有效数据个数
            int[][] newSparseArray = new int[countValid+1][3];
            newSparseArray[0][0]=twoDimensionArray.length;
            newSparseArray[0][1]=twoDimensionArray[0].length;
            newSparseArray[0][2]=countValid;
            //定义稀疏数组行初始值
            int line = 1;
            //遍历二维数组,将有效数据传入到稀疏数组中
            for(int i=0;i<twoDimensionArray.length;i++){
                for(int j=0;j<twoDimensionArray[i].length;j++){
                    if(twoDimensionArray[i][j]!=0){
                        newSparseArray[line][0]=i;
                        newSparseArray[line][1]=j;
                        newSparseArray[line][2]=twoDimensionArray[i][j];
                        line++;
                    }
                }
            }
            return newSparseArray;
        }
        return null;
    }
    /**
     * 稀疏数组转变为二维数组
     * 实现逻辑:
     *      1.根据稀稀数组第一行的信息,创建二维数组。
     *      2.遍历二维数组,将信息取出,放入到二维数组中。
     *      3.返回二维数组
     * @param sparseArray
     * @return
     */
    public static int[][] changeSparseArrayToTwoDimension(int[][] sparseArray){
        //判断稀疏数组是否有效
        if(sparseArray.length>0 && sparseArray[0].length>0){
            //根据二维数组创建稀疏数组
            int[][] twoDimensionArray = new int[sparseArray[0][0]][sparseArray[0][1]];
            //遍历稀疏数组,将有效数据取出,存入到二维数组
            for(int i=0;i<sparseArray.length;i++){
                //第一行非有效数据,不取
                if(i>0){
                    //二维数组有效元素的行数是,i行0列,二维数组的列数是i行1列,二维数组有效元素值是i行2列
                    twoDimensionArray[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
                }
            }
            return twoDimensionArray;
        }
        return null;
    }
}


三、总结和思考



1.可以不记录原始数组的行数和列数吗?


我们发现即使不记录原始数组的行数和列数,我们依然可以实现将原始数组的所有有效信息进行保存并还原,但是若是我们不清楚原始数据的行数和列数,是做不到将原始数组百分百还原的,我们能还原的只是有效数据,所以这是不可取的,显然不记录原始数组的行数和列数是不行的。


2.学习稀疏数组收获了什么


学习稀疏数组,笔者感觉主要的收获就是一种思想,化繁为简的思想,摒弃垃圾数据,只保留有效数据的细想。然后存储这部分有效信息,在需要时将这些信息返显,甚至天马星空的来说,我们都可以用稀疏数组实现加密,因为别人并不清楚这是个稀疏数组,也不清楚你这里存了啥,当然了如果将稀疏数组应用于加密,我们就不应该考虑空间和时间的性能消耗了,因为他的初衷是加密而不是节约空间的存储了。


相关文章
C4.
|
1月前
|
存储 Java 数据处理
Java的数组
Java的数组
C4.
11 0
|
3天前
|
存储 Java 程序员
Java 数组
4月更文挑战第16天
|
25天前
|
Java
java 8 数组转字符串并以逗号分隔
java 8 数组转字符串并以逗号分隔
11 0
|
1月前
|
Java
【Java】数组中的拷贝方法与初步理解深浅拷贝
【Java】数组中的拷贝方法与初步理解深浅拷贝
12 0
|
1月前
|
存储 Java C语言
【Java】以数组为例简单理解引用类型变量
【Java】以数组为例简单理解引用类型变量
14 1
|
1月前
|
存储 Java 索引
Java数组
Java数组
7 0
|
1月前
|
Java
java中判断数组中元素出现的次数
java中判断数组中元素出现的次数
10 0
|
1月前
|
Java
java向数组中插入元素
java向数组中插入元素
9 0
|
1月前
|
存储 Java 索引
JAVA一维数组
JAVA一维数组
19 3
|
1月前
|
Java 索引
JAVA数组的常用方法
JAVA数组的常用方法
17 1