在编程中,算法的重要性不言而喻,没有算法的程序是没有灵魂的。可见算法的重要性。

然而,在学习算法之前我们需要掌握数据结构,数据结构是算法的基础。

我在大学的时候,学校里的数据结构是用C语言教的,因为对C语言也不是很了解,所以掌握得不是特别好,在网上找的一些学习资料里也基本都是用C语言来进行数据结构的教学。

那么,从本篇文章开始,我将用Java语言来介绍数据结构,当然,数据结构过后就是算法。

线性结构和非线性结构
  1. 线性结构

    线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系;

    线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中存储的元素是连续的;

    链式存储的线性表称为链表,链表中存储的元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息;

    线性结构常见的有:数组、队列、链表和栈
  2. 非线性结构

    非线性结构包括:二维数组、多维数组、广义表、树结构、图结构
稀疏数组

对数据结构有了一个初步的认识之后,我们开始对一些具体的数据结构进行详细的分析。

我们来看一个实际的需求:

这是一个五子棋的程序,有存盘退出和续上盘的功能,如下图,如何将下图的棋局进行保存呢?



那这个问题很简单,很多人可能会想到用二维数组来进行存储。



如上图,我们用0表示无子,1表示黑子,2表示蓝子,但是这个程序问题很大,因为该二维数组的很多值都是默认值0,因此记录了很多没有意义的数据,那么这个时候我们就可以使用稀疏数组来对该二维数组进行一个压缩。

那么稀疏数组到底是什么呢?

当一个数组中大部分元素是0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。

稀疏数组的处理方法是:

  1. 记录数组一共有几行几列,有多少个不同的值
  2. 把具有不同值的元素的行列以及值记录在一个小规模的数组中,从而缩小程序的规模

那么了解了稀疏数组的概念后,我们通过稀疏数组来改进一下五子棋程序。



经过稀疏数组的压缩之后,原数组从原来的11行11列变为了三行三列。

该稀疏数组的第一行记录的是原数组的行数和列数以及元素个数。

接下来的每一行记录的是有效元素的位置和值,例如第二行记录的是原数组中位于1,2位置上的元素1;第三行记录的是原数组中位于2,3位置上的元素2。

综上所述,二维数组转稀疏数组的思路:

  1. 遍历原始的二维数组,得到要保存的有效元素个数
  2. 根据有效元素个数创建稀疏数组sparseArr
  3. 将二维数组的有效数据存入稀疏数组即可

稀疏数组转原始二维数组的思路:

  1. 先读取稀疏数组的第一行,根据第一行的数据创建原始二维数组
  2. 读取稀疏数组后几行的数据,并赋给原始的二维数组即可

关于实现思路已经分析完毕,接下来用代码实现。

将二维数组转稀疏数组用代码实现如下:

public static void main(String[] args) {
// 创建一个原始的二维数组(11行11列)
// 0:表示没有棋子
// 1:表示黑子
// 2:表示蓝子
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2; System.out.println("原始的二维数组:"); for (int[] row : chessArr1) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
} // 将二维数组转稀疏数组
// 先遍历二维数组,得到非0的元素个数
int sum = 0;
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
sum++;
}
}
} // 创建对应的稀疏数组
int sparseArr[][] = new int[sum + 1][3];
// 给稀疏数组赋值
// 稀疏数组第一行存的是原始数组的行数、列数和有效元素个数
sparseArr[0][0] = chessArr1.length;
sparseArr[0][1] = chessArr1[0].length;
sparseArr[0][2] = sum; // 遍历二维数组,将非0的值存入到稀疏数组中
int count = 0; // 用于记录是第几个非0数据
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
count++;
sparseArr[count][0] = i; // 存放元素位置
sparseArr[count][1] = j; // 存放元素位置
sparseArr[count][2] = chessArr1[i][j];// 存放元素值
}
}
} //遍历稀疏数组
System.out.println();
System.out.println("稀疏数组:");
for (int[] row : sparseArr) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
}
}

运行结果如下:

原始的二维数组:
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 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 稀疏数组:
11 11 2
1 2 1
2 3 2

这样,我们就成功地将二维数组转为了稀疏数组。

那么用代码如何将稀疏数组转为二维数组呢?

		// 将稀疏数组转为二维数组
// 先读取稀疏数组的第一行,根据第一行的数据创建原始数组
int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]]; // 读取稀疏数组后几行数据(从第二行开始读取),并赋给原始数组
for (int i = 1; i < sparseArr.length; i++) {
// 第一列和第二列组成元素位置,第三列为元素值
chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
} // 遍历恢复后的二维数组
System.out.println();
System.out.println("恢复后的二维数组:");
for (int[] row : chessArr2) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
}

思路缕清除之后,代码非常简单,看运行效果:

原始的二维数组:
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 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 稀疏数组:
11 11 2
1 2 1
2 3 2 恢复后的二维数组:
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 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

整体代码如下:

public static void main(String[] args) {
// 创建一个原始的二维数组(11行11列)
// 0:表示没有棋子
// 1:表示黑子
// 2:表示蓝子
int chessArr1[][] = new int[11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2; System.out.println("原始的二维数组:"); for (int[] row : chessArr1) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
} // 将二维数组转稀疏数组
// 先遍历二维数组,得到非0的元素个数
int sum = 0;
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
sum++;
}
}
} // 创建对应的稀疏数组
int sparseArr[][] = new int[sum + 1][3];
// 给稀疏数组赋值
// 稀疏数组第一行存的是原始数组的行数、列数和有效元素个数
sparseArr[0][0] = chessArr1.length;
sparseArr[0][1] = chessArr1[0].length;
sparseArr[0][2] = sum; // 遍历二维数组,将非0的值存入到稀疏数组中
int count = 0; // 用于记录是第几个非0数据
for (int i = 0; i < chessArr1.length; i++) {
for (int j = 0; j < chessArr1[i].length; j++) {
if (chessArr1[i][j] != 0) {
count++;
sparseArr[count][0] = i; // 存放元素位置
sparseArr[count][1] = j; // 存放元素位置
sparseArr[count][2] = chessArr1[i][j];// 存放元素值
}
}
} // 遍历稀疏数组
System.out.println();
System.out.println("稀疏数组:");
for (int[] row : sparseArr) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
} // 将稀疏数组转为二维数组
// 先读取稀疏数组的第一行,根据第一行的数据创建原始数组
int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]]; // 读取稀疏数组后几行数据(从第二行开始读取),并赋给原始数组
for (int i = 1; i < sparseArr.length; i++) {
// 第一列和第二列组成元素位置,第三列为元素值
chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
} // 遍历恢复后的二维数组
System.out.println();
System.out.println("恢复后的二维数组:");
for (int[] row : chessArr2) {
for (Integer value : row) {
System.out.printf("%d\t", value);
}
System.out.println();
}
}

图解Java数据结构之稀疏数组的更多相关文章

  1. Java数据结构之稀疏数组(Sparse Array)

    1.需求 编写的五子棋程序中,有存盘退出和续上盘的功能.因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据,为了压缩存储所以采用稀疏数组. 2.基本介绍 当一个数组中大部分元素为0,或者为 ...

  2. 图解 Java 数据结构

    图解Java数据结构: 一.链表       Java ListNode     https://www.cnblogs.com/easyidea/p/13371863.html 二.栈       ...

  3. 数据结构(1):稀疏数组使用java实现

    主要是用于数组压缩,去除无效的数组内容: 原数组内容: 0 0 0 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 转换成 稀疏数组 5 5 2 1 1 1 2 ...

  4. Java数据结构和算法 - 数组

    Q: 数组的创建? A: Java中有两种数据类型,基本类型和对象类型,在许多编程语言中(甚至面向对象语言C++),数组也是基本类型.但在Java中把数组当做对象来看.因此在创建数组时,必须使用new ...

  5. golang数据结构之稀疏数组

    掌握知识: 数组的初始化和赋值 结构体的初始化和赋值 字符串和整型之间的转换以及其它的一些操作 类型断言 读取文件 写入文件 对稀疏数组进行压缩 package main import ( " ...

  6. JAVA数据结构--ArrayList动态数组

    在计算机科学中,动态数组,可扩展数组,可调整数组,动态表,可变数组或数组列表是一种随机存取可变大小列表数据结构,允许添加或删除元素.它提供许多现代主流编程语言的标准库.动态数组克服了静态数组的限制,静 ...

  7. (二)Java数据结构和算法——数组

    一.数组的实现 上一篇博客我们介绍了一个数据结构必须具有以下基本功能: ①.如何插入一条新的数据项 ②.如何寻找某一特定的数据项 ③.如何删除某一特定的数据项 ④.如何迭代的访问各个数据项,以便进行显 ...

  8. 图解Java数据结构之队列

    本篇文章,将对队列进行一个深入的解析. 使用场景 队列在日常生活中十分常见,例如:银行排队办理业务.食堂排队打饭等等,这些都是队列的应用.那么队列有什么特点呢? 我们知道排队的原则就是先来后到,排在前 ...

  9. java数据结构系列之——数组(1)

    import javax.management.RuntimeErrorException; public class MyArray { private long array[]; private ...

随机推荐

  1. 测试文档(final)

    1 引言 1.1编写目的 编写本测试计划的目的是: (1)     为整个测试阶段的管理工作和技术工作提供指南同时确定测试的内容和范围,为评价系统提供依据: (2)     此外还帮助安排测试活动,说 ...

  2. [20190910]索引分支块中TERM使用什么字符表示.txt

    [20190910]索引分支块中TERM使用什么字符表示.txt --//做索引块转储,一些root,分支节点出现TERM,从来没有关注使用字符表示,简单探究看看. 1.环境:SCOTT@test01 ...

  3. HOW TO: Setting up Encrypted Communications Channels in Oracle Databas

    access_timeSeptember 22, 2015 person_outlineMartin Rakhmanov share In this article, I will explain h ...

  4. 6. Go语言—字符串操作

    一.字符串支持的转义字符 \r 回车符(返回行首) \n 换行符(直接跳到下一行的同列位置) \t 制表符 \' 单引号 \" 双引号 \\ 反斜杠 \uXXXX Unicode字符码值转义 ...

  5. c# 第36节 接口的声明

    本节内容: 1:字面理解接口 2:计算机的接口是什么呢 3:接口的声明 4:接口的注意点 1:字面理解接口 大家现在手机上,可能有很多app软件,比如天气预报的软件,有很多种,什么墨迹天气啊之类的等等 ...

  6. python3.5.3rc1学习二

    #if else示例x = 5y = 8 if x > y: print("x is greater than y")else: print("x is not g ...

  7. R-时空可视化

    Robert J. Hijmans37 开发了 raster 包用于网格空间数据的读.写.操作.分析和建模,同时维护了空间数据分析的网站 https://www.rspatial.org Edzer ...

  8. 剑指Offer-3.从尾到头打印链表(C++/Java)

    题目: 输入一个链表,按链表从尾到头的顺序返回一个ArrayList. 分析: 很简单的一道题,其实也就是从尾到头打印链表,题目要求返回ArrayList,其实也就是一个数组. 可以将链表中的元素全部 ...

  9. angular 使用ng-zorro的from组件 运行报错

    emplate parse errors: Can't bind to 'formGroup' since it isn't a known property of 'form'. 原因:没有导入表单 ...

  10. 记 2019蓝桥杯校内预选赛(JAVA组) 赛后总结

    引言 好像博客好久没更新了 哈哈哈哈哈 趁现在有空更新一波 不知道还有没有人看 确实该记录一下每天做了什么了 不然感觉有些浑浑噩噩了 比赛介绍 全称: 蓝桥杯全国软件和信息技术专业人才大赛 蓝桥杯 实 ...