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

目录

一、用数组实现顺序表


一、用数组实现顺序表

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

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

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

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

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

    //获取元素个数
    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方法来扩容。

    //新增元素,尾插
    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方法,方便我们后期打印这个顺序表。

    @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();
    }

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

    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();
    }

 下面是调试结果

运行结果 

       下面我们进行对新增元素的实现,当我们插入一个新元素时,这个新元素之后的所有元素都要后移一个位置。那么此时我们的时间复杂度就是O(N)

 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)。其实呢,对于我们的顺序表和链表这样的结构来说,它们都是“有序的”,如果我们把原有的顺序改变了,就不是原来的结构了。

//根据下标获取元素
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的时候,就会出现异常。

        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("下标越界: " + index);
        }
    //根据下标删除元素
    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--的操作。

    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);
    }

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

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

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

    //根据数组的值删除元素
    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两个方法的代码逻辑非常相似,也比较简单,就不作过多讲解。

    //判断元素是否存在
    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;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922260.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YonBuilder移动开发鸿蒙版本编译教程

0.YonBuilder移动开发应用详情页访问路径 登录用友开发者中心&#xff0c;鼠标悬浮右上角昵称处&#xff0c;点击「工作台」进入「开发者中心工作台」 「开发者中心工作台」页面点击左侧竖直菜单面板中「移动应用开发」后&#xff0c;选择右侧页面内的目标应用&#xff0c;即可…

kafka进阶_3.消费消息

文章目录 一、消费消息概览1.1、基本代码1.2、消费过程 二、消费者组2.1、push & pull2.2、消费者组 三、调度器Coordinator四、消费者分配策略五、偏移量offset5.1、起始偏移量5.2、指定偏移量消费5.3、偏移量提交5.3.1、自动提交5.3.2、手动提交 5.4、偏移量的保存 六、消…

(笔记,自己可见_1)简单了解ZYNQ

1、zynq首先是一个片上操作系统&#xff08;Soc&#xff09;&#xff0c;结合了arm&#xff08;PS&#xff09;和fpga&#xff08;PL&#xff09;两部分组成 Zynq系统主要由两部分组成&#xff1a;PS&#xff08;Processing System&#xff09;和PL&#xff08;Programmable L…

c语言的qsort函数理解与使用

介绍&#xff1a;qsort 函数是 C 标准库中用于排序的快速排序算法函数。它的用法非常灵活&#xff0c;可以对任意类型的元素进行排序&#xff0c;只要提供了比较函数即可。 qsort 函数原型及参数解释&#xff1a; void qsort ( void* base, //指向要排序的数组的首元素…

【淘汰9成NLP面试者的高频面题】LSTM中的tanh和sigmoid分别用在什么地方?为什么?

博客主页&#xff1a; [青松] 本文专栏: NLP 大模型百面百过 【淘汰9成NLP面试者的高频面题】LSTM中的tanh和sigmoid分别用在什么地方&#xff1f;为什么&#xff1f; 重要性&#xff1a;★★★ &#x1f4af; 本题主要考察面试者对以下问题的理解&#xff1a; ① 数据特征和模…

JWT加解密应用方案设计与实现

为什么要用令牌技术&#xff1f; 这个问题其实问的就是Cookice、Session、Token(令牌)之间的区别了。 首先&#xff0c;存放的位置做一下比较&#xff0c;Cookice小饼干存放在客户端的浏览器当中&#xff0c;Session会话存放在服务器线程当中(本质上还是需要利用Cookice实现)…

数据集-目标检测系列- 安全背心 检测数据集 safety_vests >> DataBall

数据集-目标检测系列- 安全背心 检测数据集 safety DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 贵在坚持&#xff01; 数据样例项目地址&#xff1a; * 相关项目 1&#xff09;数据集可视化项目&#xff1a;gi…

C语言菜鸟入门·关键字·int的用法

目录 1. int关键字 1.1 取值范围 1.2 符号类型 1.3 运算 1.3.1 加法运算() 1.3.2 减法运算(-) 1.3.3 乘法运算(*) 1.3.4 除法运算(/) 1.3.5 取余运算(%) 1.3.6 自增()与自减(--) 1.3.7 位运算 2. 更多关键字 1. int关键字 int 是一个关键字&#xff0…

unity中:超低入门级显卡、集显(功耗30W以下)运行unity URP管线输出的webgl程序有那些地方可以大幅优化帧率

删除Global Volume&#xff1a; 删除Global Volume是一项简单且高效的优化措施。实测表明&#xff0c;这一改动可以显著提升帧率&#xff0c;甚至能够将原本无法流畅运行的场景变得可用。 更改前的效果&#xff1a; 更改后的效果&#xff1a; 优化阴影和材质&#xff1a; …

Vue + Websocket播放PCM(base64转ArrayBuffer、 字符串转ArrayBuffer)

文章目录 引言I 音视频处理相关概念和APIII 案例:基于开源库 pcm-player方式播放借助MediaSource和Audio对象播放音频流。基于原生api AudioContext 播放操作III 格式转换js字符串转ArrayBufferbase64 转 ArrayBufferIV 解决pcm-player分片播放问题引言 需求: 基于webscoket传…

【JavaEE进阶】SpringBoot 快速上⼿

了解Maven,并配置国内源 使⽤SpringBoot创建⼀个项⽬, 输出HelloWorld 一、Maven 1.什么是Maven 官⽅对于Maven的描述: Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can man…

QT QFormLayout控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例&#xff0c;包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizonta…

PCA算法所体现的核心数学思维

一、PCA算法的基本思想 PCA算法的核心思想是通过线性变换&#xff0c;将数据从原始的高维空间投影到低维空间&#xff0c;同时尽可能保留数据的主要变异性。这种变换是通过找到一组新的坐标轴&#xff08;即主成分&#xff09;来实现的&#xff0c;这些坐标轴是原始数据空间的…

如何解决pdf.js跨域从url动态加载pdf文档

摘要 当我们想用PDF.js从URL加载文档时&#xff0c;将会因遇到跨域问题而中断&#xff0c;且是因为会触发了PDF.js和浏览器的双重CORS block&#xff0c;这篇文章将会介绍&#xff1a;①如何禁用pdf.js的跨域&#xff1f;②如何绕过浏览器的CORS加载URL文件&#xff1f;②如何使…

C语言数据结构——详细讲解 双链表

从单链表到双链表&#xff1a;数据结构的演进与优化 前言一、单链表回顾二、单链表的局限性三、什么是双链表四、双链表的优势1.双向遍历2.不带头双链表的用途3.带头双链表的用途 五、双链表的操作双链表的插入操作&#xff08;一&#xff09;双链表的尾插操作&#xff08;二&a…

Java小白成长记(创作笔记二)

目录 序言 思维导图 续 用户登录/注册 数据表 实体层 持久层 服务层 认证与授权 整合springsecurity controller注册测试 controller登录测试 跨域解决 方法 Java小白成长记&#xff08;创作笔记一&#xff09; Java小白成长记&#xff08;创作笔记二&#xff09;…

案例研究|阿特斯的JumpServer分布式部署和多组织管理实践

苏州阿特斯阳光电力科技有限公司&#xff08;以下简称为阿特斯&#xff09;是一家集太阳能光伏组件制造和为全球客户提供太阳能应用产品研发、设计、制造、销售的专业公司。 阿特斯集团总部位于加拿大&#xff0c;中国区总部位于江苏省苏州市。通过全球战略和多元化的市场布局…

20241123-四元数高阶奇异值分解-(1)

四元数高阶奇异值分解及其在彩色图像处理中的应用-(1) &#x1f4d4; 声明 &#x1f1e8;&#x1f1f3; : 1️⃣ &#x1f4c3; 原文网址链接: 四元数高阶奇异值分解及其在彩色图像处理中的应用 - ScienceDirect &#x1f517; Quaternion … image processing (arxiv.org) ​ …

游戏引擎学习第20天

视频参考:https://www.bilibili.com/video/BV1VkBCYmExt 解释 off-by-one 错误 从演讲者的视角&#xff1a;对代码问题的剖析与修复过程 问题的起因 演讲者提到&#xff0c;他可能无意中在代码中造成了一个错误&#xff0c;这与“调试时间标记索引”有关。他发现了一个逻辑问题…

python开发之Linux

文章目录 1. 基础2. 进阶链接压缩/解压缩 文件权限用户远程操作编辑文件软件安装 1. 基础 # 查看当前目录下文件 ls# 查看当前目录 pwd# 清除界面内容 clear# 切换目录 cd# 创建目录 mkdir# 创建文件 touch 文件 vi 文件# 强制删除 rm -rf # 复制文件 cp 复制文件 复制文件路径…