图解数据结构(01) -- 数组

本文详细介绍了数组这一基本数据结构,包括数组的概念、内存存储方式、读取和更新元素、插入与删除操作,以及数组的优势和劣势。强调数组在随机访问上的高效性,但在插入和删除操作上效率较低,适合读操作频繁的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是数组

数组对应的英文是 array,是有限个相同类型的变量所组成的有序集合,数组中 的每一个变量被称为元素。数组是最为简单、最为常用的数据结构;以整型数组为例,数组的存储形式如下图所示:
在这里插入图片描述
数组中的每一个元素有着自己的下标,只不过这个下标从0开始,一直到数组长度-1;数组的另一个特点,是在内存中顺序存储,因此可以很好地实现逻辑上的顺序表。

数组在内存中的顺序存储:内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。在 这些内存单元中,有些被其他数据占用了,有些是空闲的;
数组中的每一个元素,都存储在小小的内存单元中,并且元素之间紧密排列, 既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。
在这里插入图片描述
如图,橙色的格子代表空闲的存储单元,灰色的格子代表已占用的存储单元,而红色的连续格子代表数组在内存中的位置。不同类型的数组,每个元素所占的字节个数也不同。

2、数组的基本操作

【1】读取元素

对于数组来说,读取元素是最简单的操作。由于数组在内存中顺序存储,所以只要给出一个数组下标,就可以读取到对应的数组元素。
假设有一个名称为array的数组,要读取数组下标为3的元素,就写作 array[3];读取数组下标为5的元素,就写作array[5]。需要注意的是,输入的下 标必须在数组的长度范围之内,否则会出现数组越界,像这种根据下标读取元素的方式叫作 随机读取,简单的代码示例如下:

int[] array = new int[]{3,1,2,5,4,9,7,2};
 // 输出数组中下标为3的元素 
 System.out.println(array[3]);
【2】 更新元素

要把数组中某一个元素的值替换为一个新值,直接利用数组下标就可以把新值赋给该元素,代码示例如下:

int[] array = new int[]{3,1,2,5,4,9,7,2};
 // 给数组下标为5的元素赋值 
 array[5] = 10;
 // 输出数组中下标为5的元素
System.out.println(array[5]);  

数组读取元素和更新元素的时间复杂度都是O(1)

【3】 插入元素

数组的实际 元素数量有可能小于数组的长度,例如下面的情形:
在这里插入图片描述
因此,插入数组元素的操作存在3种情况:

  • 尾部插入
    尾部插入,是最简单的情况,直接把插入的元素放在数组尾部的空闲位置即可,等同于更新元素的操作
    在这里插入图片描述
  • 中间插入
    由于数组的每一个元素都有其固定下标,所以不得不首先把插入位置及后面的元素向后移动,腾出地方,再把要插入的元素放到对应的数组位置上;
    在这里插入图片描述
    中间插入操作的完整实现代码如下:
public class MyArray {
    private int[] array;
    private int size;
    public MyArray(int capacity){
        this.array = new int[capacity];
        size = 0;
    }
//    数组插入元素
    public void insert(int element, int index) throws Exception {
        //判断访问下标是否超出范围
        if(index<0 || index>size){
            throw new IndexOutOfBoundsException("超出数组实际元素范围!");
        }
        //从右向左循环,将元素逐个向右挪1位
        for(int i=size-1; i>=index; i--){
            array[i+1] = array[i];
        }
        //腾出的位置放入新元素
        array[index] = element;
        size++;
    }

//    输出数组
    public void output(){
        for(int i=0; i<size; i++){
            System.out.println(array[i]);
        }
    }
    public static void main(String[] args) throws Exception {
        MyArray myArray = new MyArray(10);
        myArray.insert(3,0);
        myArray.insert(7,1);
        myArray.insert(9,2);
        myArray.insert(5,3);
        myArray.insert(6,1);
        myArray.output();
    }
}

代码中的成员变量size是数组实际元素的数量,如果插入元素在数组尾部,传入的下标参数index等于size;如果插入元素在数组中间或头部,则index小于 size;
如果传入的下标参数index大于size或小于0,则认为是非法输入,会直接抛出异常

  • 超范围插入
    假如现在有一个长度为6的数组,已经装满了元素,这时还想插入一个新元素:
    在这里插入图片描述
    上述操作涉及数组的扩容,可以创建一个新数组,长度是旧数组的2倍,再把旧数组中的元素统统复制 过去,这样就实现了数组的扩容:
    在这里插入图片描述
    插入元素方法需要改写,改写后的代码如下:
public class MyArray {
    private int[] array;
    private int size;
    public MyArray(int capacity){
        this.array = new int[capacity];
        size = 0;
    }
    //    数组插入元素;element 插入的元素;index 插入的位置
    public void insert(int element, int index) throws Exception {
        //判断访问下标是否超出范围
        if(index<0 || index>size){
            throw new IndexOutOfBoundsException("超出数组实际元素范围!");
        }
        // 如果实际元素达到数组容量上限,则对数组进行扩容
        if(size >= array.length){
            resize();
        }
        //从右向左循环,将元素逐个向右挪1位
        for(int i=size-1; i>=index; i--){
            array[i+1] = array[i];
        }
        //腾出的位置放入新元素
        array[index] = element;
        size++;
    }
    //数组扩容
    public void resize(){
        int[] arrayNew = new int[array.length*2];
    // 从旧数组复制到新数组
        System.arraycopy(array,0,arrayNew,0,array.length);
        array = arrayNew;
    }
    //    输出数组
    public void output(){
        for(int i=0; i<size; i++){
            System.out.println(array[i]);
        }
    }
    public static void main(String[] args) throws Exception {
        MyArray myArray = new MyArray(10);
        myArray.insert(3,0);
        myArray.insert(7,1);
        myArray.insert(9,2);
        myArray.insert(5,3);
        myArray.insert(6,1);
        myArray.output();
    }
}

输出:
在这里插入图片描述

【4】 删除元素

数组的删除操作和插入操作的过程相反,如果删除的元素位于数组中间,其后的元素都需要向前挪动1位:
在这里插入图片描述
删除操作代码如下:

public class delete {
    private int[] array;
    private int size;
    public int delete(int index) throws Exception{
//        判断访问下标是否超出范围
        if(index<0 || index>=size){
            throw new IndexOutOfBoundsException("超出数组实际元素范围!");
        }
        int deletedElement = array[index];
        
        //从左向右循环,将元素逐个向右挪1位
        for(int i=index; i<size-1; i++){
            array[i] = array[i+1];
        }
        size--;
        return deletedElement;
    }
}

删除操作,只涉及元素的移动,时间复杂度是O(n)

如果对数组元素没有顺序要求,删除操作还存在一种取巧的方法:
如下图所示,需要删除的是数组中的元素2,可以把最后一个元素复制到元素2所在的位置,然后再删除掉最后一个元素
在这里插入图片描述
这样一来,无须进行大量的元素移动,时间复杂度降低为O(1)。

3、数组的优势和劣势

优势:
数组拥有非常高效的随机访问能力,只要给出下标,就可以用常量时间找到对应元素。有一种高效查找元素的算法叫作二分查找, 就是利用了数组的这个优势。
劣势:
数组的劣势体现在插入和删除元素方面。由于数组元素连续紧密地存储在内存中,插入、删除元素都会导致大量元素被迫移 动,影响效率。
总结:
数组所适合的是读操作多、写操作少的场景!

—————————————————————————————————————————
内容来源:《漫画算法》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值