算法与数据结构 - 数组详解
文章目录
- 前言
- 引言
- 一、场景模拟
- 二、数组介绍
- 2.1 什么是线性表
- 2.2 什么是数组
- 2.3 数组的特点
- 2.4 优缺点
- 三、图话数组
- 3.1 数组的创建过程
- 3.2 数据的插入过程
- 3.3 数组扩容
- 四、拓展概念
- 4.1 多维数组
- 4.1.1 二维数组模型
- 4.2 压缩数组(稀疏数组)
- 五、算法实战
- 5.1 普通数组(寻找数组的中心索引)
- 5.2 二维数组
- 结语
前言
点赞再看,养成习惯!
引言
周六一早李雷就来到了韩梅梅的家里,原因无外乎两人相约今天一起去逛街。不过由于来的太早,韩梅梅还没有准备好出门,李雷只能坐在韩梅梅旁看着她化妆。李雷发现,虽然韩梅梅的换妆品很多,但是她却可以准确的找到每一种需要的化妆品,好奇促使着李雷向韩梅梅询问。
李雷: 老韩,你有这么多口红,是怎么精确的找到你需要的色号呢?
韩梅梅: 因为有口红收纳箱啊,他是一个一个连续的格子组成的,我可以将所有口红按照色号的顺序存放进去。
李雷: 这么神奇的吗,那其他化妆品呢?
韩梅梅: 其他化妆品也一样啊,不同的化妆品都有不同的收纳箱,这样我无论需要哪种换妆品,只需要去寻找对应的收纳箱就好了。
李雷: 你真聪明!
一、场景模拟
其实在计算机中也有个类似于收纳箱的数据结构,那就是数组 。在详细讲述数组前,我们先模拟下刚刚说到的场景:
public class Demo {public static void main(String[] args) {String[] LipstickArr = new String[]{"迪奥","香奈儿","TF","阿玛尼","YSL"};System.out.println("韩梅梅准备展示她的口红收纳盒:");for (String lipstick: LipstickArr) {System.out.println(lipstick);}}
}
运行结果:
二、数组介绍
2.1 什么是线性表
在说数组之前,我们要理解一个概念:线性表。
线性表是数据结构中最基本,最简单的一种。一个线性表是由n个具有相同特性的元素
组成的有序序列
。
而我们今天要学习的数组就是线性表的一种。
2.2 什么是数组
数组和线性表的关系就像是父子一样,数组更像是线性表的儿子,它继承了线性表特性的同时又拥有自己的特性。什么是数组呢?
数组(Array)是一种线性数据结构,它是由一段连续的内存单元组成,用来存储具有相同特性的数据。
2.3 数组的特点
既然我们说数组具有线性表的所有特性,因此我们可以简单的推断数组具备:
1. 数组是有序序列,数组中存储的元素是按照固定的位置一个接一个的排列的(这里强调的是逻辑上的有序而并非是物理空间上的连续)。
2. 数组用来存放的元素一定是具有相同特性的。
接下来我们还要说一些专属于数组的特性
3. 数组是由一段连续的内存空间组成的,这导致了我们需要在创建数组的时候就为它指定大小,如果数组内的元素超出数组大小的时候,数组是无法扩容的,我们只能够创建一个新的更大的数组并将之前的元素复制进新数组中。
4. 由于数组采用连续的内存空间存放的,这也为它带来了最大的优点:允许随机访问
。
2.4 优缺点
优点:
- 由于数组空间是连续的,因此我们拥有了随机访问的特性,增加了查询效率。
- 由于保存的是具有相同特性的元素,因此可以更加方便的体现元素间的关联性(如排序)。
缺点 - 数组长度固定,无法动态扩容。
三、图话数组
3.1 数组的创建过程
下图是一个简易的内存单元模型图
接下来我们需要创建一个容量为7的数组:
根据数组的特性我们晓得数组是一个连续且大小固定的内存结构
,因此我们创建的数组在内存中的体现就是下图蓝色部分:
当然这只是最简单的模型,真实的内存中不会所有的内存单元都是空闲的。
3.2 数据的插入过程
我们现在需要将元素a
插入的数组中,如果我们没有特意强调,元素a
会被数组安排到第一个空闲的空间中,如下图:
有的同学会问我为什么说特别强调呢?还记得之前我们提到过的由于数组空间是连续的,因此当我们晓得我们的数组第一个元素内存地址的时候,我们就可以通过简单的算术清楚剩下的其他所有的元素地址,因此我们就可以直接通过内存单元的地址直接去修改指定空间的元素。这也就是我们说的数组支持随机访问。
简单举例,我们可以直接为数组第三个空闲地址(下标为2)的空间插入元素b
:
3.3 数组扩容
首先,数组长度是固定的,并没有办法再原有数组的基础上扩容。
假定我们的数组被填满了元素:
此时我们想要插入元素h
会发现此时数组已经没有空间可以插入了,那么此时我们就需要为数组进行扩容。这里需要注意: 数组的扩容并不是简单的增加新的内存空间那么简单。由于数组是连续的内存空间,此时数组最后一个存放元素g
的内存空间后面已经被其他应用使用,因此我们无法简单的去征用后续的内存空间完成扩容。这代表着如果我们想要插入新的元素h
,就需要在其他空间创建一个全新的连续内存空间的数组,并将之前数组的元素复制进入新的空间。
四、拓展概念
4.1 多维数组
其实与其说多维数组,不如说二维数组,主要是由于除了二维数组外,其他的我们基本都不会使用。那么,什么是二维数组?
我们之前说过数组是用来存放一组具有相同特性的元素的集合,而数组与数组之间就具有相同的特性:它们都是数组。那自然而然我们可以认为,我们可以将具有相同特性的数组可以存放入另一个数组中,而这个数组就是一个二维数组。
4.1.1 二维数组模型
其实二维数组理解起来很简单,总结起来就是用一个数组去存放具有相同特点数组的引用信息,当然后续我们还会通过代码实战来更详细的演示。
注意: 二维数组又称为矩阵,行列数相等的矩阵称为方阵。对称矩阵a[i][j] = a[j][i],对角矩阵:n阶方阵主对角线外都是零元素。
4.2 压缩数组(稀疏数组)
就像它的名字一样,压缩数组实际上是一种用来减少常规数组空间消耗的特殊应用。稀疏数组平时其实大家很少能遇到,它最长见于保存棋盘信息的程序应用中。
我们先看个最简单的应用:如下图所见,这是一个5×5的方格,我们需要用程序来模拟它的状态
常见实现(二维数组-java):
public class Demo {public static void main(String[] args) {/* 我们用0代表白色,1代表黑色*/int[][] arr = {{0,0,0,0,0},{0,1,0,0,0},{0,0,0,0,0},{0,0,0,1,0},{0,0,0,0,0}};for (int[] vertical:arr) {for (int transverse:vertical ) {System.out.print(transverse + " ");}System.out.println("");}}
}
效果:
我们没有办法说这里有什么错误,但是假定我们是个500*500的方格,显然我们就需要使用250000个内存单元来保存这个二维数组,我们有没有办法优化呢?当然有!
我们通过观察可以发现,当前数组大多数元素为相同元素,因此我们可以将这部分元素省略,只保存不同的部分。
常见的稀疏数组一般格式如下:
- 稀疏数组本身也是个二维数组
- 数组的首个元素用来存放三个信息(总行数,总列数,总变更元素数)
- 第二个开始存放变更的元素信息(行数,列数,真实值)
我们将上面的数组转换为稀疏数组存放:
稀疏数组(简单实现-java):
int [][] arr ={{5,5,2},{1,1,1},{3,3,1}};
当然我们如果想要看效果,我们将压缩数组转换为传统二维数组:
public class Demo {public static void main(String[] args) {int[][] arr = {{5, 5, 2}, {1, 1, 1}, {3, 3, 1}};int[][] oldArr = new int[arr[0][0]][arr[0][1]];for (int i = 1; i < arr.length; i++) {oldArr[arr[i][0]][arr[i][1]] = arr[i][2];}for (int[] vertical : oldArr) {for (int transverse : vertical) {System.out.print(transverse + " ");}System.out.println("");}}
}
效果:
五、算法实战
5.1 普通数组(寻找数组的中心索引)
题目:
https://leetcode.cn/problems/find-the-middle-index-in-array/
给你一个整数数组 nums ,请计算数组的 中心下标 。
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。
这一点对于中心下标位于数组最右端同样适用。
如果数组有多个中心下标,应该返回 最靠近左边 的那一个。
如果数组不存在中心下标,返回 -1 。
输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。
题解:
这个题很简单,就是计算前缀和后缀和作比较即可。(当然计算总和再去找找平均数也可以)
class Solution {public int findMiddleIndex(int[] nums) {int preSum = 0 ;int sufSum = 0 ;for (int i = 0; i < nums.length; i++) {for (int j = nums.length-1; j >i ; j--) {sufSum += nums[j] ;}if (preSum == sufSum){return i ;}else {sufSum = 0 ;preSum+= nums[i] ;}}return -1;}
}
执行结果:
5.2 二维数组
题目:
https://leetcode.cn/problems/rotate-image/
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。
请不要 使用另一个矩阵来旋转图像。
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
题解:
这个题的难点其实就在于原地算法,如果直接旋转很难保证元素能够原地改变。因此我们可以用一种取巧的办法:先水平互换,然后按照对角线对折交换:
public class Rotate {public static void main(String[] args) {int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};rotate(matrix);}private static void rotate(int[][] matrix) {//纵向反转int mark = matrix.length ;for (int i = 0; i < mark; i++) {int[] arr = matrix[(matrix.length-1)-i];matrix[(matrix.length-1)-i] = matrix[i];matrix[i] = arr ;mark--;}// 中轴线对换for (int i = 0; i < matrix.length; i++) {for (int j = i; j < matrix[i].length; j++) {if(i == j){continue;}matrix[i][j] = matrix[i][j]+matrix[j][i];matrix[j][i] = matrix[i][j] - matrix[j][i] ;matrix[i][j] = matrix[i][j] - matrix[j][i] ;}}}
}
结果:
结语
今天的内容就到此结束了,有疑问的小伙伴欢迎评论区留言或者私信博主,博主会在第一时间为你解答。
Spring通用架构及工具已上传到gitee仓库,需要的小伙伴们可以自取:
https://gitee.com/xiaolong-oba/common-base
码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~
如果大家有什么意见和建议请评论区留言或私聊博主,博主会第一时间反馈的哦。
算法与数据结构 - 数组详解相关推荐
- KMP算法之next数组详解
KMP算法之next数组详解 KMP算法实现原理 KMP算法是一种非常高效的字符串匹配算法,下面我们来讲解一下KMP算如何高效的实现字符串匹配.我们假设如下主串和模式串: int i;//i表示主串的 ...
- JavaScript算法与数据结构——字典详解
字典是一种以键-值对的形式存储数据的数据结构,接下来我们将使用JavaScript实现字典数据结构. 1.定义字典类 由于比较字典数据结构比较简单,就直接上代码好了. class Dictionary ...
- KMP算法的Next数组详解(转)
转载请注明来源,并包含相关链接. 网上有很多讲解KMP算法的博客,我就不浪费时间再写一份了.直接推荐一个当初我入门时看的博客吧: http://www.cnblogs.com/yjiyjige/p/3 ...
- 算法与数据结构 - 排序详解
目录 前言 引言 业务场景 代码模拟 1. 冒泡排序 1.1 什么是冒泡排序 1.2 图解冒泡 1.3 代码编写 1.4 总结分析 时间复杂度 是否为原地排序 2. 选择排序 2.1 什么是选择排序 ...
- JavaScript数据结构与算法——数组详解(下)
1.二维与多维数组 JavaScript只支持一维数组,但是通过在数组里保存数组元素的方式,可以轻松创建多维数组. 1.1 创建二维数组 二维数组类似一种由行和列构成的数组表格,在JavaScript ...
- JavaScript数组结构与算法——数组详解(中)
迭代器方法 在上篇中,我们探讨了很多数组方法,接下来总结一下最后一组方法--迭代器方法.这些方法对数组的每个元素应用一个函数,可以返回一个值.一组值.或者一个新数组. 1.不生成新数组的迭代器方法 以 ...
- 【数据结构】树状数组详解(Leetcode.315)
前言 最近做题时遇到一个关于树状数组的题力扣https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/但是CSDN上仅有 ...
- 天津理工大学《操作系统》实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解
天津理工大学<操作系统>实验二,存储器的分配与回收算法实现,代码详解,保姆式注释讲解 实验内容 1. 本实验是模拟操作系统的主存分配,运用可变分区的存储管理算法设计主存分配和回收程序,并不 ...
- 算法经典“钓鱼”问题详解 基于贪心算法 C语言描述
算法经典"钓鱼"问题详解 基于贪心算法 初始条件 在一条水平路边,有 n 2 ≤ n ≤ 25个钓鱼池,从左到右编号为1.2.3.--.n.小明有H1 ≤ H ≤ 16个小时的空余 ...
最新文章
- python基础===拆分字符串,和拼接字符串
- C/MFC如何获得应用程序当前路径(整理)
- jQuery框架学习第一天:开始认识jQuery
- max7219c语言,51单片机+MAX7219数码管显示C程序
- 读书笔记——计算机网络CN
- 创建与管理Oracle的分区表和本地索引
- 站长必须懂得技能:给网站设置ICO图标
- 全网首发:怎样制作CDKEY(5)-让CDKEY更混乱
- 基于ssm的图书馆预约占座系统 java mysql
- Cisco iOS的两种配置文件(思科命令的保存)
- python绘制隐含波动率曲面_使用python+tushare计算期权隐含波动率并作图
- 2017年-应届毕业生面试总结(二)
- 中国水产科学研究院教授黄樟翰走进伊宅购集团考察伊家田园项目
- c语言字符串的小程序,微信小程序字符串转换为数字如何实现
- WMI与CIM的区别
- 【李峋的爱心代码5带文字】
- rabbitmq和erlang版本对应关系
- 活期理财每日计算利率
- java 路灯感应器_Processing互动之红外热释与感应路灯
- windows常用doc命令