数据结构(Java版)第五期:ArrayList与顺序表(下)

目录

一、用数组实现顺序表


一、用数组实现顺序表

我们提到过,顺序表是基于数组的封装,这次我们以int为例,用数组去实现一个顺序表。

```java
public class MyArrayList {
    private int[] arr;
    public MyArrayList(int capacity){//指定初始容量
        arr = new int[capacity];//这个顺序表依然是空的,还得使用add往里面添加有效元素
    }
}
```

既然存在有效与无效,那我们该如何区分呢?我们可以定义一个size变量。

```java
private int size;//规定前size个元素为有效元素
```

下面就是进行写出方法,比如增、删、查、改等。

```java
    //获取元素个数
    public int size(){
        return size;
    }

    //新增元素,尾插
    public void add(int val){

    }

    //任意位置新增元素
    public void add(int index,int val){

    }

    //根据下标获取元素
    public void get(int index){

    }

    //根据下标设置元素
    public void set(int index,int val){

    }

    //根据数组的值删除元素
    public void delete(int val){

    }

    //根据下标删除元素
    public int remove(int index,int val){

    }

    //判断元素是否存在
    public boolean contains(int val){

    }

    //查找元素所在位置
    public int indexOf(int index){

    }

    //删除所有元素
    public void clear(){

    }

    //打印操作,把ArrayList转成字符串

    @Override
    public String toString() {

    }
```

这里是一些主要的方法,如果我们想实现其他方法,也可以再新增。我们对方法进行了命名之后,下面就是进行实现。我们先来看新增元素的实现。我们需要把新增的元素放到最后的位置,下标为size。如果说size比arr.length的值要小,那么我们正常添加就可以,可如果我们size的值比arr.length要小,我们要先进行扩容。我们可以再写一个resize方法来扩容。

```java
    //新增元素,尾插
    public void add(int val){
        if(size == arr.length){
            resize();
        }
        arr[size] = val;
        size++;
    }
    private void resize(){//这个方法只有程序员才能调用
        //创建一个更长的数组,长度扩大到原来的1.5倍
        //这样就能保证我们每添加一个元素,长度够用
        int[] newArr = new int[(int)(arr.length * 1.5)];
        for (int i = 0; i < size; i++) {
            //原来数组的元素复制到新数组中
            newArr[i] = arr[i];
            //新数组代替旧数组,旧的数组会被垃圾回收给释放掉
            arr = newArr;
        }
    }

```

我们还要实现toString方法,方便我们后期打印这个顺序表。

```java
    @Override
    public String toString() {
        // 打印的格式形如: [1, 2, 3, 4]
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("[");
        for (int i = 0; i < size; i++) {
            stringBuilder.append(arr[i]);
            if (i < size - 1) {
                stringBuilder.append(", ");
            }
        }
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
```

下面我们来进行一下测试,

```java
    public static void test(){
        MyArrayList list = new MyArrayList(6);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        System.out.println(list.size);
        System.out.println(list);
    }

    public static void main(String[] args) {
        test();
    }
```

下面是调试结果数据结构(Java版)第五期:ArrayList与顺序表(下)

运行结果

数据结构(Java版)第五期:ArrayList与顺序表(下) 下面我们进行对新增元素的实现,当我们插入一个新元素时,这个新元素之后的所有元素都要后移一个位置。那么此时我们的时间复杂度就是O(N)

```java
 public void add(int index, int val) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("Index out of bounds");
        }
        // 如果数组已经满了, 继续添加元素, 也是要先扩容
        if (size == arr.length) {
            resize();
        }
        // 搬运元素的操作. 从后往前, 依次把每个元素都往后搬运一个位置.
        // 如果元素本身的下标是 i, 就把这个元素赋值到 i+1 的位置上.
        // index 位置的元素也需要往后搬运一下, i >= index
        for (int i = size - 1; i >= index; i--) {
            arr[i + 1] = arr[i];
        }
        // 此时就相当于把 index 位置已经腾出来了. 把新元素放到 index 位置上就好了.
        arr[index] = val;
        // 不要忘记更新 size
        size++;
    }
```

有的老铁可能觉得这个方法有点麻烦,如果说我们直接插入的新元素,这个位置的旧元素直接后移到我们顺序表中的最后一个位置行不行,并且此时的时间复杂度就是O(N)。其实呢,对于我们的顺序表和链表这样的结构来说,它们都是“有序的”,如果我们把原有的顺序改变了,就不是原来的结构了。

```java
//根据下标获取元素
public int get(int index){
        if(index<0 || index>arr.length){
            throw new IndexOutOfBoundsException("下标越界"+index);
        }
        return arr[index];
    }

```

数组通过[]访问下标,时间复杂度是O(1)。Java的数组呢,本质是来源于C/C++,C/C++中数组访问下标时间复杂度同样也是O(1)。我们知道数组是一块连续存放的内存空间,通过过数组首元素和下标快速算出来对应元素的地址,根据这个地址来访问这个内存空间。内存的硬件设备具备一个特性,随机访问能力。

对于删除元素,也是要保证顺序表在有序的前提下,对于尾删的情况,直接进行size--就可以了,不必进行搬运。 但如果说,我们删除中间的某一个元素,当index==size时,尾插是可以的,但index>size的时候,就会出现异常。

```java
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("下标越界: " + index);
        }
```


```java
    //根据下标删除元素
    public int remove(int index,int val){
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("下标越界: " + index);
        }
        //要删除的元素先保存起来,就不怕后期搬运的时候被覆盖
        int result = arr[index];
        if (index == size-1){
            //尾删
            size--;
            return result;
        }
        //一定要注意边界值,可以代入具体数字进行验证
        for (int i = index; i < size-1; i++) {
            arr[i] = arr[i+1];//进行向后搬运
        }
    }
```

仔细分析上面的代码时就会发现,当index为尾删的时候,for循环是进不去的,接下来进行的就是size--的操作。

```java
    public static void test2(){
        MyArrayList list = new MyArrayList(10);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        list.remove(0);
        System.out.println(list);
        list.remove(2);
        System.out.println(list);
        list.remove(4);
        System.out.println(list);
        list.remove(100);
        System.out.println(list);
    }
```

数据结构(Java版)第五期:ArrayList与顺序表(下)

对于delete方法,我们需要先找到这个值所在的位置,找到之后,调用remove方法执行。

```java
        for (int i = 0; i < size; i++) {
            if(arr[i] == val){
                //找到了
                break;
            }
        }
```

这个for循环结束的条件有两个,i==size或者是arr[i]==val时,但由于我们这个i是for循环里面的局部变量,我们可以把i改成index,并作用再for循环之外。

```java
    //根据数组的值删除元素
    public void delete(int val){
        int index = 0;
        for (; index < size; index++) {
            if(arr[index] == val){
                //找到了
                break;
            }
        }
        for (int i = index; i < size-1; i++) {
            arr[i] = arr[i+1];//进行向后搬运
        }
    }
```

对于contains方法和IndexOf两个方法的代码逻辑非常相似,也比较简单,就不作过多讲解。

```java
    //判断元素是否存在
    public boolean contains(int val){
        for (int i = 0; i < size; i++) {
            if (arr[i] == val) {
                return true;
            }
        }
        return false;
    }

    //查找元素所在位置
    public int indexOf(int val){
        for (int i = 0; i < size; i++) {
            if (arr[i] == val) {
                return i;
            }
        }
        return -1;
    }
```

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/5003.html

(0)
LomuLomu
上一篇 2024 年 12 月 31 日 上午3:15
下一篇 2024 年 12 月 31 日 上午4:16

相关推荐

  • 华为OD机试E卷 –字符串变换最小字符串 –24年OD统一考试(Java & JS & Python & C & C++)

    文章目录 题目描述 输入描述 输出描述 用例 题目解析 JS算法源码 java算法源码 python算法源码 c算法源码 c++算法源码 题目描述 给定一个字符串s,最多只能进行一次变换,返回变换后能得到的最小字符串(按照字典序进行比较)。 变换规则:交换字符串中任意两个不同位置的字符。 输入描述 一串小写字母组成的字符串s 输出描述 按照要求进行变换得到的…

    未分类 2025 年 1 月 12 日
    55200
  • 利用Java与GeoTools实现矢量边界自动生成地理网格的技术方案

    目录背景概述一、数据准备与实现原理1、矢量数据预处理2、网格生成技术原理二、具体编码实现1、获取Shapefile边界范围2、构建网格要素集合3、输出Shapefile文件三、成果检验与评估1、输出文件格式说明2、GIS软件验证方法四、技术总结与展望 背景概述 在数字地理信息处理领域,空间数据的转换与处理技术日益重要。矢量数据以其精确的空间表达能力广泛应用于…

    2025 年 5 月 19 日
    1.2K00
  • 一文搞懂架构设计的衡量标准:功能性、可用性、性能、可扩展性、安全性、协作效率、复杂度、成本效益

    大家好,我是汤师爷~ 架构设计的首要目标是服务于业务需求。因此,我们不应该盲目追求所谓的”最厉害的”架构,而应该致力于寻找最适合当前业务环境和未来发展需求的架构方案。 衡量架构的合理性是一个复杂的过程,需要从多个角度进行全面评估。主要可以从以下视角进行分析: 功能需求视角:评估架构是否有效支撑当前业务需求,并具有充分的灵活性以适应未来业务发展。 非功能需求视…

    未分类 2025 年 1 月 15 日
    55200
  • 【手写 RPC】使用netty手写一个RPC框架 结合新特性 虚拟线程

    【手写RPC框架】如何使用netty手写一个RPC框架 结合新特性 虚拟线程 什么是RPC框架 RPC(Remote Procedure Call)远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC框架是一种远程调用的框架,它可以让你像调用本地方法一样调用远程方法。 避免了开发人员自己去封装网络请求、连接管理、序列…

    2025 年 1 月 13 日
    58300
  • [华为OD机考 – 密语传递 – 基于深度优先搜索的Java实现(2025 A卷 200分)]

    华为2025届OD机考A卷试题库持续更新中,专项练习 _ 戳此进入_ 专题导览 本系列试题已编入《华为OD机考Java真题全集(A/B/C/D/E卷)》。练习频次与中签率正相关, 添加哪吒微信,备注”华为OD备考”,加入专属刷题群 ,每道题配备:解题思路解析、完整代码实现、多组测试数据、算法选择依据、应用场景说明,题库实时更新,24小时在线答疑。 题目要求 …

    2025 年 5 月 12 日
    27900

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信