算法与数据结构 - 数组详解


前言

点赞再看,养成习惯!

引言

周六一早李雷就来到了韩梅梅的家里,原因无外乎两人相约今天一起去逛街。不过由于来的太早,韩梅梅还没有准备好出门,李雷只能坐在韩梅梅旁看着她化妆。李雷发现,虽然韩梅梅的换妆品很多,但是她却可以准确的找到每一种需要的化妆品,好奇促使着李雷向韩梅梅询问。
李雷: 老韩,你有这么多口红,是怎么精确的找到你需要的色号呢?
韩梅梅: 因为有口红收纳箱啊,他是一个一个连续的格子组成的,我可以将所有口红按照色号的顺序存放进去。
李雷: 这么神奇的吗,那其他化妆品呢?
韩梅梅: 其他化妆品也一样啊,不同的化妆品都有不同的收纳箱,这样我无论需要哪种换妆品,只需要去寻找对应的收纳箱就好了。
李雷: 你真聪明!


一、场景模拟

其实在计算机中也有个类似于收纳箱的数据结构,那就是数组 。在详细讲述数组前,我们先模拟下刚刚说到的场景:

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 优缺点

优点:

  1. 由于数组空间是连续的,因此我们拥有了随机访问的特性,增加了查询效率。
  2. 由于保存的是具有相同特性的元素,因此可以更加方便的体现元素间的关联性(如排序)。
    缺点
  3. 数组长度固定,无法动态扩容。

三、图话数组

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个内存单元来保存这个二维数组,我们有没有办法优化呢?当然有!
我们通过观察可以发现,当前数组大多数元素为相同元素,因此我们可以将这部分元素省略,只保存不同的部分。
常见的稀疏数组一般格式如下:

  1. 稀疏数组本身也是个二维数组
  2. 数组的首个元素用来存放三个信息(总行数,总列数,总变更元素数)
  3. 第二个开始存放变更的元素信息(行数,列数,真实值)

我们将上面的数组转换为稀疏数组存放:
稀疏数组(简单实现-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

码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~
如果大家有什么意见和建议请评论区留言或私聊博主,博主会第一时间反馈的哦。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

晓龙oba

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

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

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

打赏作者

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

抵扣说明:

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

余额充值