什么是稀疏数组?稀疏数组详解

【1】背景

如下图所示,这里有一个 15 × 15 的棋盘,如果现在要让你通过编码的方式,让你将这盘棋局保存起来,你会怎么做呢?

面对行列数据的保存,我相信大多人第一时间都会想到用二维数组进行保存。

【2】普通数组保存棋盘数据

比如,我们可以将棋盘进行抽象化,用一个 15 × 15 的二维数组来表示,然后用 0 表示空点,用 1 表示白子,用 2 表示黑子,于是就可以抽象为如下模样。

于是,我们可以通过如下代码,将数据保存到二维数组中。

/**
 * 将棋盘数据保存为二维数组
 */
public static int[][] saveChess() {
    // 初始化棋盘(大小为 15 * 15),int默认为0(即空点)
    int[][] arr = new int[15][15];
    // 保存白子(用1表示)
    arr[5][5] = 1;
    arr[7][5] = 1;
    arr[6][7] = 1;
    // 保存黑子(用2表示)
    arr[6][6] = 2;
    arr[7][6] = 2;
    arr[8][6] = 2;
    arr[7][7] = 2;
    return arr;
}

【3】普通数组的不足之处

上面,我们通过了一个最普通的方法,将棋盘数据保存在了一个二维数组中,整个数组我们用了 15 × 15(共 275)个点来保存数据,其中有 268 个点都是空的。实际来说,我们只需要将有价值的黑白子保存起来即可,因为只要我们知道黑白子数据,以及棋盘的大小,那么这268个空点是可以不用进行保存的。

把这样没有价值的数据起来,不但会浪费存储空间的大小,如果写入到磁盘中,还会增大 IO 的读写量,影响性能,这就是用普通二维数组来表示棋盘数据的不足之处。

那么,针对以上情况,我们该如何将我们的数据格式进行优化呢?于是就引出了我们接下来的稀疏数组的概念。

【4】稀疏数组

所谓稀疏数组,从字面上我们也可以得知,它仍然是一个数组,他的作用就是将一个对应的数组数据进行优化,比如,我们将上面的二维数组进行优化。

我们可以定义一个数组,只记录黑白子的数据,具体如下:

从这个表中,我们可以只看到有价值的数据了,如果我们再将这个表抽象为一个二维数组,那么他的大小就是 7 * 3 = 21 个点了。

这相对之前的200多个点来说,确实少了许多节点,节省了很多存储空间。

但只这样保存也是不够的,因为我们并没有保存原棋盘的大小,那么我们不妨约定增加一行,保存原来棋盘的大小(二维数组的大小),那么我们可以用如下进行表示:

这里我们约定用第1行数据来记录原二维数组的规模,第 1 个值为原数组总行数,第 2 个值为原数组列数,第 3 个值为有价值数据的总数。

这样,我们就将原数组数据,优化为了一个稀疏数组。

【4】二维数组转稀疏数组

根据上面的原理,我们就可以通过代码方式,将原来的普通二维数组,优化为一个稀疏数组了,具体编码如下:

/**
 * 普通数组转稀疏数组
 * 
 * @param arr_普通数组
 * @return 稀疏数组
 */
public static int[][] castSparseArray(int[][] arr) {
    // 原数组的总行数
    int row = arr.length;
    // 原数组的总列数
    int cloumn = arr[0].length;
    // 获取黑白子总数
    int sum = 0;
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length; j++) {
            if (arr[i][j] != 0) {
                sum++;
            }
        }
    }
    // 创建稀疏数组(sum+1 行表示 sum 个黑白子 + 1行规模)
    int[][] sparseArray = new int[sum + 1][3];
    // 第 1 行原二维数组的行、列、棋子总数
    sparseArray[0][0] = row;
    sparseArray[0][1] = cloumn;
    sparseArray[0][2] = sum;
    // 第 2 行开始存具体数据(每行都是一个棋子数据)
    int sparseRow = 0;
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length; j++) {
            if (arr[i][j] != 0) {
                sparseRow++;
                sparseArray[sparseRow][0] = i;
                sparseArray[sparseRow][1] = j;
                sparseArray[sparseRow][2] = arr[i][j];
            }
        }
    }
    return sparseArray;
}

【5】稀疏数组转二维数组

我们用稀疏数组保存数据,主要是为了节省磁盘空间,优化 IO 读写性能,但在最终复原棋盘的时候,我们还是需要将稀疏数组的数据转化为原普通二维数组数据,那么我们又将如何通过编码的方式去实现稀疏数组转二维数组呢?对应编码如下:

/**
 * 稀疏数组转普通数组
 * 
 * @param sparseArr_稀疏数组
 * @return 普通二维数组
 */
public static int[][] castToArray(int[][] sparseArr) {
    // 获取稀疏数组第 1 行(记录了原数组的总行数和总列数)
    int[] rowFirst = sparseArr[0];
    // 初始化数组(棋盘)
    int row = rowFirst[0];
    int column = rowFirst[1];
    int[][] arr = new int[row][column];
    // 初始化数据(黑白子)
    for (int i = 1; i < sparseArr.length; i++) {
        int[] sparseRow = sparseArr[i];
        arr[sparseRow[0]][sparseRow[1]] = sparseRow[2];
    }
    return arr;
}

这样,我们就可以将稀疏数组还原为普通数组了。

【总结】

在存储数组数据的时候,我们如果存在许多默认值的数据,不妨用稀疏数组来进行存储,这样数据相对来说就会瘦小很多,不但可以节省磁盘空间,还可以优化磁盘读写时性能。

最后附上相关的整段代码:

/**
 * 二维数组与稀疏数组
 * 
 * @author ZhangYuanqiang
 * @since 2021年5月13日
 */
public class SparseArrayTest {

    public static void main(String[] args) {
        // 用二维数组保存棋盘
        int[][] arr = saveChess();
        // 打印二维数组
        print(arr);
        // 将二维数组转化为稀疏数组
        int[][] sparseArr = castSparseArray(arr);
        // 打印稀疏数组
        print(sparseArr);
        // 稀疏数组转二位数组
        int[][] arr2 = castToArray(sparseArr);
        print(arr2);
    }

    /**
     * 打印普通数组
     */
    public static void print(int[][] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + "\t");
            }
            System.out.println();
        }
        System.out.println("--------------------------------------");
    }

    /**
     * 将棋盘数据保存为二维数组
     */
    public static int[][] saveChess() {
        // 初始化棋盘(大小为 15 * 15)
        int[][] arr = new int[15][15];
        // 保存白子(用1表示)
        arr[5][5] = 1;
        arr[7][5] = 1;
        arr[6][7] = 1;
        // 保存黑子(用2表示)
        arr[6][6] = 2;
        arr[7][6] = 2;
        arr[8][6] = 2;
        arr[7][7] = 2;
        return arr;
    }

    /**
     * 普通数组转稀疏数组
     * 
     * @param arr_普通数组
     * @return 稀疏数组
     */
    public static int[][] castSparseArray(int[][] arr) {
        // 原数组的总行数
        int row = arr.length;
        // 原数组的总列数
        int cloumn = arr[0].length;
        // 获取黑白子总数
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (arr[i][j] != 0) {
                    sum++;
                }
            }
        }
        // 创建稀疏数组(sum+1 行表示 sum 个黑白子 + 1行规模)
        int[][] sparseArray = new int[sum + 1][3];
        // 第 1 行原二维数组的行、列、棋子总数
        sparseArray[0][0] = row;
        sparseArray[0][1] = cloumn;
        sparseArray[0][2] = sum;
        // 第 2 行开始存具体数据(每行都是一个棋子数据)
        int sparseRow = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (arr[i][j] != 0) {
                    sparseRow++;
                    sparseArray[sparseRow][0] = i;
                    sparseArray[sparseRow][1] = j;
                    sparseArray[sparseRow][2] = arr[i][j];
                }
            }
        }
        return sparseArray;
    }

    /**
     * 稀疏数组转普通数组
     * 
     * @param sparseArr_稀疏数组
     * @return 普通二维数组
     */
    public static int[][] castToArray(int[][] sparseArr) {
        // 获取稀疏数组第 1 行(记录了原数组的总行数和总列数)
        int[] rowFirst = sparseArr[0];
        // 初始化数组(棋盘)
        int row = rowFirst[0];
        int column = rowFirst[1];
        int[][] arr = new int[row][column];
        // 初始化数据(黑白子)
        for (int i = 1; i < sparseArr.length; i++) {
            int[] sparseRow = sparseArr[i];
            arr[sparseRow[0]][sparseRow[1]] = sparseRow[2];
        }
        return arr;
    }
}

 

 

 

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页