数据结构之稀疏数组

一、实际案例

如何高效的存储一局刚开始的棋局,并且在将来可以无损恢复
在这里插入图片描述
简单的方式将棋盘抽象成二维数据,未落子用0表示,黑子用1,白子用2;该问题就转换成如何持久化一个二维数组。序列化、IO皆可
s p a r s e = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] sparse=\begin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 2 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ \end{bmatrix} sparse=000000000000000000000001000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
这数学中上述矩阵被称为:稀疏矩阵,即具有很多相同的元素的矩阵,若不做任何处理直接存储显然不够高效。

二、基本介绍

2.1 压缩稀疏数组

如何高效的存储稀疏数组呢?
第一步:获取稀疏数组的规格,即行列信息
第二步:获取稀疏数组非0元素个数
第三步:创建压缩数组用于存储
根据上述的数组可得行列为11,非0元素个数为2,那么可以创建下列压缩数组
z i p = [ 11 11 2 1 2 1 2 3 2 ] zip=\begin{bmatrix} 11 & 11 & 2 \\ 1 & 2 & 1 \\ 2 & 3 & 2 \\ \end{bmatrix} zip=11121123212
解释:
zip第一行记录spares的行数、列数、非0元素个数;
zip剩余每行第一列记录非0元素所在行,第二列记录非0元素所在列,第三列记录非0元素值

代码实现思路:

  1. 遍历稀疏数组获取有效数据个数sum
  2. 根据sum创建压缩数组zip[sum+1][3]
  3. 将稀疏数组有效数据填入zip

代码如下

// 压缩数组
public static int[][] zipArray(int[][] arr) {
    // 遍历稀疏数组得到非0数据个数
    int sum = 0;
    for (int[] ints : arr) {
        for (int j = 0; j < arr.length; j++) {
            if (ints[j] != 0)
                sum++;
        }
    }

    // 创建压缩数组
    int[][] zipArr = new int[sum + 1][3];

    // 稀疏数组赋值
    zipArr[0][0] = arr.length;
    zipArr[0][1] = arr[0].length;
    zipArr[0][2] = sum;

    int line = 1;
    // 再次遍历获取非0数据
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr[i].length; j++) {
            if (arr[i][j] != 0) {
                zipArr[line][0] = i;
                zipArr[line][1] = j;
                zipArr[line][2] = arr[i][j];
                line++;
            }
        }
    }
    
    return zipArr;
}

2.2 解压稀疏数组

解压还原稀疏数组就很简单了
第一步:获取稀疏数组规格
第二步:从压缩数组中获取数据填入稀疏数组即可

代码实现思路:

  1. 读取压缩数组第一行,创建原始二维稀疏数组
  2. 读取压缩数组后几行赋值即可

代码如下

// 解压数组
public static int[][] unzipArray(int[][] arr) {
    // 创建原始数组
    int[][] srcArr = new int[arr[0][0]][arr[0][1]];
    for (int i = 1; i < arr.length; i++) {
        srcArr[arr[i][0]][arr[i][1]] = arr[i][2];
    }

    return srcArr;
}

三、补充

3.1 测试代码

package 稀疏数组;

/**
 * Project: data structure
 * Package: 稀疏数组
 * Version: 1.0
 * Author: wjun
 * <p>
 * Description:
 * Created by hc on 2021/01/27 17:22
 * © 1996 - 2021 Zhejiang Hong Cheng Computer Systems Co., Ltd.
 */
public class SparseArray {
    public static void main(String[] args) {
        int[][] sparseArr = new int[11][11];
        sparseArr[1][1] = 1;
        sparseArr[2][3] = 2;

        int[][] zipArray = zipArray(sparseArr);

        for (int[] ints : zipArray) {
            for (int j = 0; j < zipArray.length; j++) {
                System.out.print(ints[j] + "\t");
            }
            System.out.println();
        }

        int[][] unzipArray = unzipArray(zipArray);

        for (int[] ints : unzipArray) {
            for (int j = 0; j < unzipArray.length; j++) {
                System.out.print(ints[j] + "\t");
            }
            System.out.println();
        }
    }

    // 压缩数组
    public static int[][] zipArray(int[][] arr) {
        // 遍历稀疏数组得到非0数据个数
        int sum = 0;
        for (int[] ints : arr) {
            for (int j = 0; j < arr.length; j++) {
                if (ints[j] != 0)
                    sum++;
            }
        }

        // 创建压缩数组
        int[][] zipArr = new int[sum + 1][3];

        // 稀疏数组赋值
        zipArr[0][0] = arr.length;
        zipArr[0][1] = arr[0].length;
        zipArr[0][2] = sum;

        int line = 1;
        // 再次遍历获取非0数据
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (arr[i][j] != 0) {
                    zipArr[line][0] = i;
                    zipArr[line][1] = j;
                    zipArr[line][2] = arr[i][j];
                    line++;
                }
            }
        }

        return zipArr;
    }

    // 解压数组
    public static int[][] unzipArray(int[][] arr) {
        // 创建原始数组
        int[][] srcArr = new int[arr[0][0]][arr[0][1]];
        for (int i = 1; i < arr.length; i++) {
            srcArr[arr[i][0]][arr[i][1]] = arr[i][2];
        }

        return srcArr;
    }
}

3.2 研究

问题:当有效数据数量达到多少时直接存储原始数组(此时成稀疏数组就不合适了)?
设原始数组为m x n且有a个有效数字,得到的压缩数据则为a+1 x 3,有如下推导:
m n < ( a + 1 ) ∗ 3 = > a < m n 3 − 1 mn < (a+1)*3 => a < \frac {mn} 3 - 1 mn<(a+1)3=>a<3mn1
当有效数组接近整个数组的三分之一时,再使用该方法将得不偿失,且该方法本身就是以时间换取空间

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王是个弟弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值