数据结构与算法学习(一):线性表之数组的插入与删除(Java 实现)

2023-05-16

文章目录

  • 一、数组介绍
    • 1、线性表
    • 2、连续的内存空间和类型相同的数据
  • 二、利用数组实现插入操作及相应的时间复杂度分析
    • 1、数组原本有顺序,插入后需要继续保持数组有序
      • (1)思路分析
      • (2)代码实现
      • (3)时间复杂度分析
    • 2、不需要保证数组的顺序,直接在数组末尾插入
      • (1)思路分析
      • (2)代码实现
      • (3)时间复杂度分析
    • 3、在数组指定位置插入
      • (1)思路分析
      • (2)代码实现
      • (3)时间复杂度分析
  • 三、利用数组实现删除操作及相应的时间复杂度分析
    • 1、直接删除指定下标的元素
      • (1)思路分析
      • (2)代码实现
      • (3)时间复杂度分析
  • 四、完整代码

一、数组介绍

数组是一种线性表,用连续的内存空间存储类型相同的数据元素。

像线性表、连续的内存空间都是什么意思呢?下面分别解释一下:

1、线性表

线性表的所有数据元素最多只有前和后两个方向的数据结构。

常见的线性表有数组、链表、队列、栈等。

非线性数据结构比较常见的是二叉树、图等。

2、连续的内存空间和类型相同的数据

在新建一个数组时,就会向内存申请一块连续的空间,这一块连续的空间会划分为相同大小的小块,在感觉上如下图所示:

在这里插入图片描述
因为放的是 int 类型数据,所以每块所占的内存的大小都为 4 字节。

正式因为数组有连续的内存空间和类型相同的数据,所以才拥有它最突出的特性。它支持利用下标进行随机访问。

二、利用数组实现插入操作及相应的时间复杂度分析

1、数组原本有顺序,插入后需要继续保持数组有序

(1)思路分析

首先为了保证插入后数组还是有序,需要找到数组应该插入的位置 k 。

找到插入位置 k 后,需要将 k ~ n - 1 个数组元素都向后移一位,将 k 的位置空下来。

然后插入元素即可。

(2)代码实现

 /**
     * 有序插入 (顺序为从小到大)       平均时间复杂度为 O(n),最好时间复杂度为 O(1),最坏时间复杂度为 O(n)
     *
     * @param e 被插入元素
     */
    public void addOrdered(int e) throws RuntimeException {
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        // 记录元素位置
        int k = -1;

        // 当前数组没有元素,直接插入
        if (getSize() == 0) {
            data[0] = e;
            setSize(getSize() + 1);
            return;
        }


        if (e < data[0]) {
            k = 0;
        } else if (e > data[getSize() - 1]){
            k = getSize();
        } else {
            // 遍历数组找到元素顺序
            for (k = 1; k < getSize(); k++) {
                if (e < data[k]) {
                    break;
                }
            }
        }


        // 将 k ~ n - 1 的数向后挪
        for (int i = getSize() - 1; i >= k; i--) {
            data[i + 1] = data[i];
        }

        // 将 e 插入 k 位置
        data[k] = e;
        // 元素个数加 1
        setSize(getSize() + 1);
    }

(3)时间复杂度分析

最好时间复杂度为 O(1)(当需要将元素插入在数组末尾时,不需要挪动其他的数组元素)

最坏时间复杂度为 O(n)(当需要将元素插入在数组头部时,需要挪动 n - 1 个数组元素)

平均时间复杂度为 O(n)(可能插入的位置的概率都是一样的,为 1/n,所以平均时间复杂度为 (1 + 2 + … n) / n = O(n))

2、不需要保证数组的顺序,直接在数组末尾插入

(1)思路分析

由于不需要保证数组顺序,所以直接在数组末尾插入。

(2)代码实现

  /**
     * 插入元素到末尾 时间复杂度为 O(1)
     * @param e
     */
    public void add(int e) {
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        data[getSize()] = e;
        setSize(getSize() + 1);
    }

(3)时间复杂度分析

时间复杂度为 O(1)。

3、在数组指定位置插入

(1)思路分析

如果需要插入的位置为 k ,需要将 k ~ n - 1 个数组元素都向后移一位,将 k 的位置空下来。

然后插入元素即可。

(2)代码实现

 /**
     * 在指定位置插入
     *
     * @param index 下标,从 0 开始
     * @param e
     */
    public void add(int index, int e) {
        // 判断传入的下标是否合法
        if (index < 0 || index >= getCapacity()) {
            throw new RuntimeException("传入的数组下标不合法");
        }
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        if (size != 0) {
            if (index < getSize()) {
                for (int i = getSize(); i > index; i--) {
                    data[i] = data[i - 1];
                }
            }
        }
        data[index] = e;
        setSize(getSize() + 1);
    }

(3)时间复杂度分析

最好时间复杂度为 O(1)(当插入的位置是数组末尾时,或者数组没有数据时)。

最坏时间复杂度为 O(n)(插入的位置是数组首部时)。

平均时间复杂度为 O(n)(可能插入的位置的概率都是一样的,为 1/n,所以平均时间复杂度为 (1 + 2 + … n) / n = O(n))。

三、利用数组实现删除操作及相应的时间复杂度分析

1、直接删除指定下标的元素

(1)思路分析

将 k + 1 ~ 到 n -1 位置的元素向前移一位,覆盖掉删除的元素即可。

(2)代码实现

 /**
     * 删除(需要保证数据连续性)
     *
     * @param k 被删除元素的位置 (该位置从 0 开始)
     * @return 被删除元素
     */
    public int del(int k) throws RuntimeException {
        // 判断 k 的合法性
        if (k < 0 || k >= getSize()) {
            throw new RuntimeException("给定的元素位置不合法");
        }
        // 保存 k 位置的数据
        int e = data[k];
        // 将 k + 1 后的元素向前移动,覆盖 k 位置的元素,保持数组数据的连续性
        for (int i = k + 1; i < getSize(); i++) {
            data[i - 1] = data[i];
        }
        // 修改数组元素个数
        setSize(getSize() - 1);
        // 返回被删除元素
        return e;
    }

(3)时间复杂度分析

删除操作的时间复杂度的计算方式和插入类似。

最好时间复杂度是 O(1)(当删除的元素为数组末尾元素时,不需要移动数组元素)。

最坏时间复杂度是 O(n)(当删除的元素为数组头部元素时,需要将 1 ~ n-1 的元素向前移动)。

平均时间复杂度是 O(n)(需要删除的数据元素的插入的位置的概率都是一样的,为 1/n,所以平均时间复杂度为 (1 + 2 + … n) / n = O(n))

四、完整代码

package demo.array;

import java.util.Arrays;

/**
 * @author liyanan
 * @date 2019/12/25 15:02
 */
public class ArrayList {
    private int[] data;

    private int size;

    private int capacity;

    public ArrayList(int capacity) {
        this.capacity = capacity;
        size = 0;
        data = new int[capacity];
    }

    public int getSize() {
        return size;
    }

    public int getCapacity() {
        return capacity;
    }

    public void setSize(int size) {
        this.size = size;
    }

    /**
     * 有序插入 (顺序为从小到大)       平均时间复杂度为 O(n),最好时间复杂度为 O(1),最坏时间复杂度为 O(n)
     *
     * @param e 被插入元素
     */
    public void addOrdered(int e) throws RuntimeException {
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        // 记录元素位置
        int k = -1;

        // 当前数组没有元素,直接插入
        if (getSize() == 0) {
            data[0] = e;
            setSize(getSize() + 1);
            return;
        }


        if (e < data[0]) {
            k = 0;
        } else if (e > data[getSize() - 1]) {
            k = getSize();
        } else {
            // 遍历数组找到元素顺序
            for (k = 1; k < getSize(); k++) {
                if (e < data[k]) {
                    break;
                }
            }
        }


        // 将 k ~ n - 1 的数向后挪
        for (int i = getSize() - 1; i >= k; i--) {
            data[i + 1] = data[i];
        }

        // 将 e 插入 k 位置
        data[k] = e;
        // 元素个数加 1
        setSize(getSize() + 1);
    }

    public String printArrayList() {
        StringBuilder sb = new StringBuilder();
        sb.append("array = [");
        for (int i = 0; i < getSize(); i++) {
            sb.append(data[i]);
            if (i < getSize() - 1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        sb.append(", size = ");
        sb.append(getSize());
        sb.append(", capacity = ");
        sb.append(getCapacity());
        return sb.toString();
    }

    /**
     * 插入元素到末尾 时间复杂度为 O(1)
     *
     * @param e
     */
    public void add(int e) {
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        data[getSize()] = e;
        setSize(getSize() + 1);
    }

    /**
     * 在指定位置插入
     *
     * @param index 下标,从 0 开始
     * @param e
     */
    public void add(int index, int e) {
        // 判断传入的下标是否合法
        if (index < 0 || index >= getCapacity()) {
            throw new RuntimeException("传入的数组下标不合法");
        }
        // 判断是否已满,已满不能插入
        if (getSize() == getCapacity()) {
            throw new RuntimeException("当前数组元素已满,不能进行插入操作");
        }

        if (size != 0) {
            if (index < getSize()) {
                for (int i = getSize(); i > index; i--) {
                    data[i] = data[i - 1];
                }
            }
        }
        data[index] = e;
        setSize(getSize() + 1);
    }

    /**
     * 删除(需要保证数据连续性)
     *
     * @param k 被删除元素的位置 (该位置从 0 开始)
     * @return 被删除元素
     */
    public int del(int k) throws RuntimeException {
        // 判断 k 的合法性
        if (k < 0 || k >= getSize()) {
            throw new RuntimeException("给定的元素位置不合法");
        }
        // 保存 k 位置的数据
        int e = data[k];
        // 将 k + 1 后的元素向前移动,覆盖 k 位置的元素,保持数组数据的连续性
        for (int i = k + 1; i < getSize(); i++) {
            data[i - 1] = data[i];
        }
        // 修改数组元素个数
        setSize(getSize() - 1);
        // 返回被删除元素
        return e;
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构与算法学习(一):线性表之数组的插入与删除(Java 实现) 的相关文章

随机推荐

  • python 语言基础 - 你不得不知道的字符串常用函数之isalpha

    前言 小伙伴们大家好 xff0c 每天一个小知识 xff0c 一起学python每天进步一点点 不知道小伙伴们在开发的时候有没有遇到这样一种场景 xff0c 有时候一些业务需要 xff0c 想要判断一个字符串是不是由纯字符组成 xff0c
  • python 语言基础 - 你不得不知道的字符串常用函数之isdigit

    前言 小伙伴们大家好 xff0c 每天一个小知识 xff0c 一起学python每天进步一点点 上一篇文章中为大家分享了关于判断字符串是否全都是由字符组成的函数isalpha xff0c 今天要给大家分享的依然是判断字符串组成的函数isdi
  • uni-app,选择器使用

    1 uni app 选择器使用示例 xff1a let view 61 uni createSelectorQuery in this select 34 grade btn 34 view fields size true scrollO
  • CSRF漏洞修复建议

    1 关于CSRF 跨站请求伪造 xff0c xff08 Cross site request forgery xff09 是一种对网站的恶意利用 CSRF作为一种跨网站的攻击方式 xff0c 不同于XSS站内攻击 CSRF是通过伪装成受信任
  • centos6和centos7的防火墙基本命令

    一 centos6 xff1a 1 firewall的基本启动 停止 重启命令 查看防火墙状态 xff1a service iptables status etc init d iptables status centos6启动 停止防火墙
  • django中vue的使用

    转载 xff1a https blog csdn net qq 21389693 article details 105734696 后端使用vue的目的 后端使用vue的目的就是把ajax里面的数据绑定到前端 xff0c 实现动静分离 V
  • centos7更新yum源

    1 centos7安装后 xff0c 默认yum源配置文件位置 xff1a vi etc yum repos d CentOS Media repo 2 下载新的国内yum源 centos自带的是国外yum源 xff0c 下载速度相当慢 换
  • 临时出差宁波随笔

    因公司吉利项目收尾工作 xff0c 需到客户现场施工 主要负责软件项目的质量查验 xff0c 及相关问题修复 一千多公里的路程 xff0c 坐高铁7小时左右 xff0c 真是体验了中国速度 一路由西北而向东南直下 xff0c 抵达舟山群岛附
  • GitHub Copilot

    介绍 GitHub Copilot 是人工智能编程助手 xff0c 它可以帮助你编写程序 在你用visual studio或visual studio code等软件设计工具进行编程时 xff0c 它可以直接给你整行或整个方法的代码提示 x
  • 微信小程序头像昵称填写功能

    自 2022 年 10 月 25 日 24 时后 xff08 以下统称 生效期 xff09 xff0c 用户头像昵称获取规则将进行如下调整 xff1a 自生效期起 xff0c 小程序 wx getUserProfile 接口将被收回 xff
  • mysql 5.7设置数据库大小写敏感

    1 此处讲的是windows环境下的mysql配置 xff0c 小编用的是win10系统 xff1b 2 安装完mysql后 xff0c 数据库默认是大小写不敏感的 xff1b 3 修改my ini文件 xff1a C ProgramDat
  • eclipse 护眼色设置

    1 调整eclipse editor区域背景色 背景颜色向你推荐 xff1a 色调 xff1a 85 饱和度 xff1a 1 2 3 亮度 xff1a 2 0 5 文档都不再是刺眼的白底黑字 xff0c 而是非常柔和的豆沙绿色 xff0c
  • Js apply方法详解,及其apply()方法的妙用

    Js apply方法详解 我在一开始看到javascript的函数apply和call时 非常的模糊 看也看不懂 最近在网上看到一些文章对apply方法和call的一些示例 总算是看的有点眉目了 在这里我做如下笔记 希望和大家分享 如有什么
  • 如何安装双系统之ubuntu安装

    如何安装双系统之ubuntu安装 1 首先在Windows下对磁盘分出一块空闲分区大概100G左右 2 然后下载Ubuntu16 04镜像 xff0c 制作启动盘 3 重启电脑 xff0c 按住对应的键 xff08 不同电脑型号可能不同 x
  • 场景分类综述——Remote Sensing Image Scene Classification Meets Deep Learning

    一 场景分类面临的挑战 场景分类的挑战包括 xff1a 1 类内多样性大 xff0c 2 类间相似性高 也称为类间可分性低 xff0c 3 对象 场景尺度的差异大 就类内的多样性而言 xff0c 挑战主要来自于在同一个语义类中出现的地物的巨
  • 配置服务器的磁盘阵列并正确分区

    磁盘阵列 xff0c 即独立磁盘冗余阵列RAID xff08 Redundant Array of Independent Disks xff09 xff0c 其实就是一个将多块独立磁盘结合在一起 xff0c 从而提高数据的可靠性和I O性
  • 软件项目组织架构安排

    这个主题涉及到三个方面 xff0c 项目计划管理 组织管理和技术管理范畴 项目计划管理是项目管理中的一个大篇章 xff0c 包括时间计划 成本计划 费用计划等在内的各类计划管理 xff0c 不是本文章所谈的范围 xff0c 只是本文主题涉及
  • SpringBoot2.x学习(二):为属性注入配置文件中的值:@ConfigurationProperties注解的使用

    文章目录 一 64 ConfigurationProperties 简单介绍二 64 ConfigurationProperties 使用示范1 创建两个 javaBean2 在 SpringBoot 全局配置文件写入需要注入的值2 1 a
  • SpringBoot 2.x学习(三):为属性注入配置文件中的值:@Value 注解的使用

    文章目录 一 64 Value 注解的作用二 使用 64 Value 为普通成员变量注入值1 字面量 xff08 1 xff09 语法 xff08 2 xff09 举例 2 Spring 表达式 xff08 SpEL xff09 xff08
  • 数据结构与算法学习(一):线性表之数组的插入与删除(Java 实现)

    文章目录 一 数组介绍1 线性表2 连续的内存空间和类型相同的数据 二 利用数组实现插入操作及相应的时间复杂度分析1 数组原本有顺序 xff0c 插入后需要继续保持数组有序 xff08 1 xff09 思路分析 xff08 2 xff09