数据结构(一):数组

1、数组

1.1、数组定义

数组对应的英文是array,是有限个相同类型的变量所组成的有序集合,数组中的每一个变量被称为元素。

以整型数组为例,数组的存储形式如下图所示。

Snipaste_2022-03-24_15-13-37.png
数组中的 每一个元素也有着自己的下标,只不过这个下标 从0开始,一直到数组长度-1。

数组的另一个特点,是在内存中顺序存储, 因此可以很好地实现逻辑上的顺序表。

数组在内存中的顺序存储,具体是什么样子呢?

内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。在这些内存单元中,有些被其他数据占用了,有些是空闲的。

数组中的每一个元素,都存储在小小的内存单元中,并且元素之间紧密排列,既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。

Snipaste_2022-03-24_15-13-37.png
在上图中,橙色的格子代表空闲的存储单 元,灰色的格子代表已占用的存储单元,而红色 的连续格子代表数组在内存中的位置。

1.2、数组的基本操作

1.2.1、读取元素

对于数组来说,读取元素是最简单的操作。 由于数组在内存中顺序存储,所以只要给出一个 数组下标,就可以读取到对应的数组元素。

像这种根据下标读取元素的方式叫作随机读取

int[] array = new int[]{3,5,6,9};
//打印下标为1的元素
System.out.println(array[1]);

1.2.2、更新元素

要把数组中某一个元素的值替换为一个新 值,也是非常简单的操作。直接利用数组下标, 就可以把新值赋给该元素。

int[] array = new int[]{3,5,6,9};
//给下标为1的元素重新赋值
array[1] = 10;
//打印下标为1的元素
System.out.println(array[1]);

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

1.2.3、插入元素

插入数组元素的操作存在3种情况:

  • 尾部插入
  • 中间插入
  • 超范围插入
    尾部插入,是最简单的情况,直接把插入的 元素放在数组尾部的空闲位置即可,等同于更新 元素的操作。

Snipaste_2022-03-24_17-53-48.png
中间插入,稍微复杂一些。由于数组的每一 个元素都有其固定下标,所以不得不首先把插入 位置及后面的元素向后移动,腾出地方,再把要 插入的元素放到对应的数组位置上。

Snipaste_2022-03-24_17-53-48.png
代码实现如下:

public class Demo {
   private int[] array;
   private int size;
   
   public Demo(int capacity){
      array = new int[capacity];
      size = 0;
   }

   /**
    * 数组插入元素
    * @param element
    * @param index
    */
   public void insert(int element,int index){
      //判断访问下标是否超出范围
      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 ++;
   }
}

超范围插入,假如现在有一个长度为6的数组,已经装满了 元素,这时还想插入一个新元素。

Snipaste_2022-03-24_17-53-48.png
这就涉及数组的扩容了。可是数组的长度在 创建时就已经确定了。这该如何是好呢?

此时可以创建一个新数组,长度是旧数组的2倍,再把旧数组中的元素统统复制过去,这样就实现了数组的扩容。

Snipaste_2022-03-24_17-53-48.png
代码如下:

public class Demo {
   private int[] array;
   private int size;

   public Demo(int capacity){
      array = new int[capacity];
      size = 0;
   }

   /**
    * 数组插入元素
    * @param element
    * @param index
    */
   public void insert(int element,int index){
      //判断访问下标是否超出范围
      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 resize(){
      int[] arrayNew = new int[array.length * 2];
      //从旧数组复制到新数组
      System.arraycopy(array,0,arrayNew,0,array.length);
      array = arrayNew;
   }
 
}

1.2.4、删除元素

数组的删除操作和插入操作的过程相反,如果删除的元素位于数组中间,其后的元素都需要向前挪动1位。

Snipaste_2022-03-24_17-53-48.png
代码实现如下:

public class Demo {
   private int[] array;
   private int size;

   public Demo(int capacity){
      array = new int[capacity];
      size = 0;
   }

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

插入操作,数组扩容的时间复 杂度是O(n),插入并移动元素的时间复杂度也 是O(n),综合起来插入操作的时间复杂度是 O(n)。至于删除操作,只涉及元素的移动,时 间复杂度也是O(n)。

1.3、数组的优势和劣势

数组拥有非常高效的随机访问能力,只要给 出下标,就可以用常量时间找到对应元素。有一 种高效查找元素的算法叫作二分查找,就是利用 了数组的这个优势。

至于数组的劣势,体现在插入和删除元素方 面。由于数组元素连续紧密地存储在内存中,插 入、删除元素都会导致大量元素被迫移动,影响 效率。

总的来说,数组所适合的是读操作多、写操 作少的场景。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值