【再临数据结构】Day1. 稀疏数组
前言
这不单单是稀疏数组的开始,也是我重学数据结构的开始。因此,在开始说稀疏数组的具体内容之前,我想先说一下作为一个有着十余年“学龄”的学生,所一直沿用的一个学习方法:3W法。我认为,只有掌握了正确的学习方法,才能真正的达到“事半功倍”的境界。
何为3W?其实就是3问:What?Why?How?
简单解释一下:
1.What:界定问题,首先要搞清楚这个问题是什么。
若连题都读不懂,就不必说如何解题了。
2.Why:分析问题,要分析问题的本质和原因。
在读懂题意的前提下,知道它考察的方向是什么,这样后续才能顺着这个方向去解题。
3.How:解决问题,通过目标导向思维解决问题。
经过前两问的思考,想必你已经透过题目明白了这道题真正考察的是什么,那么就要考虑如何去处理、解决掉它。
1.What?
好,我们开始稀疏数组的具体内容。在了解为什么要学习它和怎样使用之前,我们要先了解稀疏数组是什么。
我们通过具体的应用场景来了解它,比如说,你用编程语言需要写一个简单的五子棋小游戏,它需要能够正常玩,并且有存盘和读取功能。
此时你能想到的问题就是:
- 如何绘制棋盘并存储棋盘上落子的坐标信息。
- 如何实现存盘和读盘的功能。
首先来思考第一个问题:总所周知,棋盘由行和列组成。那么,你很自然的想到了数学中的坐标系,将其落子点看做一组横纵坐标。因此你打算使用二维数组这一数据结构来模拟棋盘。用0代表没有落子,1代表落黑子,2代表落白字。
好,问题轻松写意地解决了。你开始写代码,作为一个有着程序员精神的人,你很快地通过百度解决了第二个问题。但你的程序员灵魂并不甘于止步于此,又想琢磨如何优化,此时迎来了一个新的问题:若一个11*11大小的棋盘,只落了两个子。此时需要存盘,如何才能尽可能多的节省存储空间?
很显然,若使用0来代表未落子的交点,那么这个二维数组中就有着许许多多的“无效数据”。只有当黑子 / 白子落在这个交点上,它才是“有效数据”。此时,稀疏数组就出现在你的面前了。
2.Why?
为什么要使用稀疏数组而不是别的数据结构?为什么它能实现对二维数组的压缩?
别急,一图以蔽之。
稀疏数组的处理方式:
1、分别记录原数组的行个数、列个数、有多少个“有效数据”。【第一行】
2、将不同有效数据的元素的行、列、值信息记录在一个小规模的数组中,从而缩小程序的规模。【其余行】
稀疏数组很简单,上面两点足以概括。
具体思路
二维数组转稀疏数组
1.遍历原始的二维数组,得到有效数据的个数 sum
2根据sum就可以创建稀疏数组 sparseArr int[sum+1][3]
注:此处之所以为[sum+1]的原因是第一列要存储原数组的大小及有效数的个数(sum)
3.将二维数组的有效数据数据存入到稀疏数组
稀疏数组转二维数组
1.先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的chessArr2= int[11][11]
⒉.在读取稀疏数组后几行的数据,并赋给原始的二维数组即可.
以上过程再配合Java的IO操作(写入磁盘文件,文件读入内存)就可以实现类似下棋游戏过程中的“存盘”,“读取” 的操作。
3.How?
二维数组转稀疏数组
//将二维数组压缩为稀疏数组public static int[][] toSparseArray(int[][] arr){//1.遍历二维数组,获取到二维数组中有效元素的个数int sum = 0; //有效元素个数//增强版双重for循环,实现遍历二维数组的操作for (int[] ints : arr) {for (int anInt : ints) {if (anInt != 0) {sum++;}}}//2.根据sum的个数及稀疏数组的设定来建立一个原数组所对应的稀疏数组int[][] sparseArray = new int[sum+1][3];//3.根据原数组的情况为稀疏数组赋值//3.1第一行存储原二维数组的信息,首先填写它的数据sparseArray[0][0] = arr.length; //原二维数组行长度sparseArray[0][1] = arr[0].length; //原二维数组列长度sparseArray[0][2] = sum; //有效数的个数//3.2其余行存储有效数在原二维数组中的行、列及数值int index = 0;for(int i=0; i<arr.length; i++){for (int j=0; j<arr[0].length; j++){if (arr[i][j] != 0){index++;sparseArray[index][0] = i;sparseArray[index][1] = j;sparseArray[index][2] = arr[i][j];}}}return sparseArray;}
稀疏数组还原为二维数组
//将稀疏数组转换为原二维数组public static int[][] toErWei(int[][] arr){//1.新建一个二维数组,读取稀疏数组的第一行,根据第一行数据初始化二维数组int[][] erWei = new int[arr[0][0]][arr[0][1]]; //此时二维数组大小确定,内容全是0//2,读取稀疏数组后几行数据,并赋给初始化好的二维数组for (int i=1;i<arr.length;i++){ //i=1表示从稀疏数组的第二行开始读取//0存储行信息,1存储列信息,2存储值信息erWei[arr[i][0]][arr[i][1]]=arr[i][2]; //认真理解}return erWei;}
源码
public class sparseArray {public static void main(String[] args) {int[][] erWei = new int[11][11];for (int i=0; i<erWei.length-1; i++){for (int j=0; j<erWei[0].length-1; j++){erWei[i][j] = 0;}}//设定有效元素erWei[2][5] = 16;erWei[1][6] = 14;erWei[7][9] = 28;erWei[4][5] = 20;erWei[1][3] = 17;erWei[6][4] = 79;//输出原二维数组printArr(erWei);//转化为稀疏数组int[][] sparseArray = toSparseArray(erWei);printArr(sparseArray);//转化为二维数组int[][] ErWei = toErWei(sparseArray);printArr(ErWei);}//将二维数组压缩为稀疏数组public static int[][] toSparseArray(int[][] arr){//1.遍历二维数组,获取到二维数组中有效元素的个数int sum = 0; //有效元素个数//增强版双重for循环,实现遍历二维数组的操作for (int[] ints : arr) {for (int anInt : ints) {if (anInt != 0) {sum++;}}}//2.根据sum的个数及稀疏数组的设定来建立一个原数组所对应的稀疏数组int[][] sparseArray = new int[sum+1][3];//3.根据原数组的情况为稀疏数组赋值//3.1第一行存储原二维数组的信息,首先填写它的数据sparseArray[0][0] = arr.length; //原二维数组行长度sparseArray[0][1] = arr[0].length; //原二维数组列长度sparseArray[0][2] = sum; //有效数的个数//3.2其余行存储有效数在原二维数组中的行、列及数值int index = 0;for(int i=0; i<arr.length; i++){for (int j=0; j<arr[0].length; j++){if (arr[i][j] != 0){index++;sparseArray[index][0] = i;sparseArray[index][1] = j;sparseArray[index][2] = arr[i][j];}}}return sparseArray;}//将稀疏数组转换为原二维数组public static int[][] toErWei(int[][] arr){//1.新建一个二维数组,读取稀疏数组的第一行,根据第一行数据初始化二维数组int[][] erWei = new int[arr[0][0]][arr[0][1]]; //此时二维数组大小确定,内容全是0//2,读取稀疏数组后几行数据,并赋给初始化好的二维数组for (int i=1;i<arr.length;i++){ //i=1表示从稀疏数组的第二行开始读取//0存储行信息,1存储列信息,2存储值信息erWei[arr[i][0]][arr[i][1]]=arr[i][2]; //认真理解}return erWei;}//输出数组信息public static void printArr(int[][] arr){for (int[] ints : arr){for (int anInt : ints) {System.out.print(anInt + " ");}System.out.println();}System.out.println("---------------------");}
}
运行结果如下:
如上图所示,二维数组压缩为稀疏数组确实极大地节省了存储空间。同样可以通过稀疏数组在读盘时将其进行复原,达到预期需求。
相关文章:

【再临数据结构】Day1. 稀疏数组
前言 这不单单是稀疏数组的开始,也是我重学数据结构的开始。因此,在开始说稀疏数组的具体内容之前,我想先说一下作为一个有着十余年“学龄”的学生,所一直沿用的一个学习方法:3W法。我认为,只有掌握了正确的…...
二十四、MongoDB 聚合运算( aggregate )
MongoDB 聚合( aggregate ) 用于处理数据,比如统计平均值,求和等。然后返回计算后的数据结果 MongoDB 聚合有点类似 SQL 语句中的 COUNT( * ) aggregate() 方法 MongoDB aggregate() 为 MongoDB 数据库提供了聚合运算 语法 aggregate() 方法的语法如下 > d…...
【C++】6.模板初阶
交换两个数 任何一个类型交换还要重新写一个函数 如何解决? 模板->写跟类型无关的函数 1.泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。 如何写一个函数适用所有类型的交换? #include &…...
Docker部署Airbyte
Linux环境部署前置要求机器配置2c4g(最低),4c8g(推荐)dockerdocker-compose (要求新版本的docker-compose)安装airbyte,打开终端,进入你想安装airbyte的目录。#Clone代码 git clone https://github.com/air…...

2023王道考研数据结构笔记第一章绪论
第一章 绪论 1.1 数据结构的基本概念 1.数据:数据是信息的载体,是描述客观事物属性的数、字符以及所有能输入到计算机中并被程序识别和处理的符号的集合。 2.数据元素:数据元素是数据的基本单位,通常作为一个整体进行考虑和处理…...

告别空指针让代码变优雅,Optional使用图文例子源码解读
一、前言 我们在开发中最常见的异常就是NullPointerException,防不胜防啊,相信大家肯定被坑过! 这种基本出现在获取数据库信息中、三方接口,获取的对象为空,再去get出现! 解决方案当然简单,只…...

【C++】哈希——unordered系列容器|哈希冲突|闭散列|开散列
文章目录一、unordered系列关联式容器二、哈希概念三、哈希冲突四、哈希函数五、解决哈希冲突1.闭散列——开放定址法2.代码实现3.开散列——开链法4.代码实现六、结语一、unordered系列关联式容器 在C98中,STL提供了底层为红黑树结构的一系列关联式容器,…...
mysql-面试
锁: mysql的锁分为全局锁、表锁、行锁、间隙锁 全局锁:Flush tables with read lock 可以全局设计库为只读 表锁:一种是表锁,一种是元数据锁(meta data lock,MDL) lock tables t1 read,t2 wi…...

【夏虫语冰】Win10局域网下两台电脑无法ping通: 无法访问目标主机
文章目录1、简介2、修改高级共享设置3、启用防火墙规则4、局域网内的其他主机访问NAT模式下的虚拟机4.1 虚拟机网络设置4.2 访问测试4.2.1 http测试4.2.2 curl测试4.2.3 telnet测试4.2.4 端口占用测试5、其他结语1、简介 ping 192.168.31.134ping主机ip时,访问无法…...

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——Join多种应用
3.7.1Reduce Join 1、工作原理 Map端的主要工作:为来自不同表或文件的key/value对,打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。 Reduce端的主要工作:在Reduc…...

SSRF漏洞原理、危害以及防御与修复
一、SSRF漏洞原理漏洞概述SSRF(Server-side Request Forge,服务端请求伪造)是一种由攻击者构造形成由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的,所…...

CV学习笔记-ResNet
ResNet 文章目录ResNet1. ResNet概述1.1 常见卷积神经网络1.2 ResNet提出背景2. ResNet网络结构2.1 Residual net2.2 残差神经单元2.3 Shortcut2.4 ResNet50网络结构3. 代码实现3.1 Identity Block3.2 Conv Block3.3 ResNet网络定义3.4 整体代码测试1. ResNet概述 1.1 常见卷积…...

百亿数据,毫秒级返回查询优化
近年来公司业务迅猛发展,数据量爆炸式增长,随之而来的的是海量数据查询等带来的挑战,我们需要数据量在十亿,甚至百亿级别的规模时依然能以秒级甚至毫秒级的速度返回,这样的话显然离不开搜索引擎的帮助,在搜…...

cpp之STL
STL原理 STL ⼀共提供六⼤组件,包括容器,算法,迭代器,仿函数,适配器和空间配置器,彼此可以组合套⽤。容器通过配置器取得数据存储空间,算法通过迭代器存取容器内容,仿函数可以协助算…...
基于Spring Boot开发的资产管理系统
文章目录 项目介绍主要功能截图:登录首页信息软件管理服务器管理网络设备固定资产明细硬件管理部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目…...
Markdown总结
文字的着重标记与段落的层次划分 Tab键可以缩进列表; shift Tab:取消缩进列表 加粗(****)、斜体(**)高亮:xxx$$:特殊标记删除:~~xxx~~多级标题:######无序列…...

字节跳动软件测试岗4轮面经(已拿34K+ offer)...
没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021年10月,我有幸成为了字节跳动的一名测试工程师,从外包辞职了历…...
docker - 搭建redis集群和Etcd
概述 由于业务需要,需要把之前的分布式架构调整成微服务,把老项目迁移到k8s的服务中,再开始编码之前,需要再本地环境里做相应的准备工作,使用docker搭建redis集群,Etcd主要是注册本地的rpc服务。 Liunx O…...

Java程序开发中如何使用lntelliJ IDEA?
完成了IDEA的安装与启动,下面使用IDEA创建一个Java程序,实现在控制台上打印HelloWorld!的功能,具体步骤如下。 1.创建Java项目 进入New Project界面后,单击New Project选项按钮创建新项目,弹出New Project对话框&…...

【Linux】理解进程地址空间
🍎作者:阿润菜菜 📖专栏:Linux系统编程 我们在学习C语言的时候,都学过内存区域的划分如栈、堆、代码区、数据区这些。但我们其实并不真正理解内存 — 我们之前一直说的内存是物理上的内存吗? 前言 我们…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...

对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...

结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...
Qt学习及使用_第1部分_认识Qt---Qt开发基本流程
前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...