数据结构--稀疏数组

概述

稀疏数组也是一种数组(总是二维的),是一种多维数组的数组压缩技术。比如存在一个10 \times 10的数组,但是数组中只有3个元素,如果要存储的话需要占100个位置。因为数组的每个位置的元素都要存储,哪怕是0或者null
\begin{array}{l:} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9\\ \hline 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 1 & 0 & 0 & 0 & \mathbf{5} & 0 & 0 & 0 & 0 & 0 & 0\\ 2 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 3 & 0 & \mathbf{3} & 0 & 0 & \mathbf{7} & 0 & 0 & 0 & 0 & 0\\ 4 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 5 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 6 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 7 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 8 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 9 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ \end{array}

稀疏数组就是为了解决这个问题的。稀疏数组的第一行存储数组的维度信息以及有效元素数。剩余行存储有效元素所在的坐标和元素的值。如上二维数组,那么采用稀疏数组存储就是:

\begin{array}{l:l:} \text{row}& \text{col} & \text{value} \\ \hline 10 & 10 & 3 \\ 1 & 3 & 5 \\ 3 & 1 & 3 \\ 3 & 4 & 7 \\ \end{array}

稀疏数组的行数是:有效元素数+1;列数是:数组维度+1。如果是一个3维数组,那就需要4列来保存数据,因为有3列要保存元素的坐标。

从上面的例子我们可以看出,稀疏数组存储的数据所占用的位置要比二维数组小很多,上面的10 \times 10的数组占了100个位置,而使用稀疏数组后仅用了16个位置。

什么情况下才可以用稀疏数组

然而稀疏数组并不总是好的,从上面的例子中我们也看出来了,稀疏数组只适合于有效数据少,但是数组较大的情况。假设一个二维数组行是m列是nt个有效数据,那么稀疏数组的行数就是t+1,列数是3,所以只有在
3(t+1) < mn; \Longrightarrow t < \frac{mn}{3} - 1
时,使用稀疏数组才能有效的对数据进行压缩。对于上面10 \times 10的数组就是32,如果超过32个有效元素,使用稀疏数组反而会增加开销。

那么推广到多维数组,假设n维数组可以存储S个元素,数组中有t个有效元素。那么稀疏数组的行数就是t+1,列数是n+1。只有在
(t+1)(n+1) < S \Longrightarrow t < \frac{S}{n+1} - 1
对于一个3维可存放1000个元素的数组,只有当有效元素小于249时,适用稀疏数组才能起到节省空间的作用。

实现

以下列出Java中二维稀疏数组的实现。

package com.codestd.study.ds;

/**
 * 稀疏数组
 *
 * @author jaune
 * @since 1.0.0
 */
public class SparseArray {

    /**
     * 将一个二维数组转成稀疏数组
     * @param arrs 二维数组
     * @return 稀疏数组
     */
    public int[][] toSparse(int[][] arrs) {
        //arrs.length;
        int row = arrs.length, col = arrs[0].length, nums = 0;
        for (int[] arr : arrs) {
            for (int i : arr) {
                if (i != 0) {
                    nums++;
                }
            }
        }
        int[][] sparseArr = new int[nums + 1][3];
        sparseArr[0][0] = row;
        sparseArr[0][1] = col;
        sparseArr[0][2] = nums;
        int index = 1;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int num = arrs[i][j];
                if (num != 0) {
                    sparseArr[index][0] = i;
                    sparseArr[index][1] = j;
                    sparseArr[index][2] = num;
                    index++;
                }
            }
        }
        return sparseArr;
    }

    /**
     * 将一个稀疏数组转为二维数组
     * @param sparseArr 稀疏数组,这里稀疏数组需要是一个二维数组的稀疏数组。
     * @return 还原后的二维数组
     */
    public int[][] parse(int[][] sparseArr) {
        int row = sparseArr[0][0],
                col = sparseArr[0][1],
                nums =  sparseArr[0][2];

        int[][] arr = new int[row][col];
        for (int i = 1; i <= nums; i++) {
            int[] ints = sparseArr[i];
            arr[ints[0]][ints[1]] = ints[2];
        }
        return arr;
    }

    /**
     * 测试一下
     * @param args
     */
    public static void main(String[] args) {
        SparseArray sparseArray = new SparseArray();
        int[][] arrs = new int[11][11];
        arrs[2][3] = 10;
        arrs[3][6] = 20;
        int[][] sparseArr = sparseArray.toSparse(arrs);
        System.out.println(sparseArr);
        int[][] sourceArr = sparseArray.parse(sparseArr);
        System.out.println(sourceArr);
    }

}

对于n \times n的二维数组,toSparse()的时间复杂度为O(2n^2) = O(n^2)(如果不知道这里为什么等于O(n^2)可以看看我个人博客上关于算法复杂度的文章)。parse()方法的时间复杂度是O(n),这个n是有效元素数(O(n+1) = O(n)

应用

如棋盘的保存。围棋或者五子棋,要把棋盘上落子的信息保存起来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容