你还不懂《顺序表》?那就不要错过这篇文章!!!

news2024/11/24 11:02:16

🎇🎇🎇作者:
@小鱼不会骑车
🎆🎆🎆专栏:
《java练级之旅》
🎓🎓🎓个人简介:
一名专科大一在读的小比特,努力学习编程是我唯一的出路😎😎😎
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

顺序表

  • 介绍线性表
  • 顺序表
    • 定义一个顺序表类(Arraylist)
    • 查找顺序表元素(indexOf)
    • 遍历顺序表(display)
    • 定义一个异常(SubscriptException)
    • 增加元素(Add)
    • 删除元素(Delete)
    • 总结

介绍线性表

在认识顺序表前我们先认识一下线性表:

小鱼曾在一本书中看到这么一个例子他是这样介绍线性表的:

在一个幼儿园中,每次放学时老师带领着小朋友们一个接着一个从教室出来,并且他们之间的次序都是一样的,例如小明排在第4位,则每次放学时他都是排在第四位,前面同样是那个的小女孩,后面也一直是那个男孩。
在这里插入图片描述

老师是这么解释的:为了保障小朋友的安全,避免漏掉小朋友,所以在出门前就安排好了他们出门的次序,谁谁在前,谁谁在后,当这样养成习惯时,如果是有哪个小朋友没有到位,则他前面的和后面的小朋友就会发现并且报告给老师,谁谁不在,老师也可以很快地清点人数,万一有人走丢,也能在最快时间知道,及时去寻找。

通过上面的例子我们就可以看出:线性表是一个序列,也可以理解为他们之间的元素是有顺序的,并且第一个元素无前驱,最后一个元素无后继,其余元素有且仅有一个前驱和后继,并且如果一个小朋友的衣服被两个或多个小朋友拉扯,这其实是在打架!不是有序排队。

总结

线性表(linear list) 是n个具有相同特性的数据元素的有限序列。
线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

顺序表

在简单介绍完线性表之后,我们可以将顺序表理解为用数组的形式来实现线性表。

那一个顺序表都会有什么操作呢?举个例子:

如果有小朋友排队的时候突然想去厕所,那么当他去厕所之后,是不是他后面的同学都向前移动一下,先将这个空位补上?
对的! 大概就是如下图的过程
在这里插入图片描述
过了一会上厕所的小朋友回来了,是不是刚刚移动的小朋友需要集体后移来给这个小朋友腾出地方啊?
就像下图:
在这里插入图片描述
我们可以将上述的两个操作理解为增加和删除元素,那我们还是不是需要有查找和修改的功能啊?

那么大体的思路我们有了,接下来就是实现顺序表了!

定义一个顺序表类(Arraylist)

我们这个顺序表用到的是数组,所以我们就可以在这个顺序表中定义一个数组的成员变量,由于顺序表中的元素没有指定是什么类型,那么我们就创建一个泛型类的顺序表,以及一个泛型数组!并且我们还需要一个值来记录数组内元素的个数,那么为了方便,我们还可以利用构造方法初始化我们的数组大小。

代码如下:

public class Arraylist<E> {

    public E[] elem;//创建这个成员变量
    public int usedSize;//0/。记录长度
    //默认容量
    private static final int DEFAULT_SIZE = 10;

    public Arraylist() {//初始化数组
        this.elem = (E[]) new Object[DEFAULT_SIZE];
    }

    public Arraylist(int p) {
        this.elem = (E[]) new Object[p];
    }
 }   

我们对构造方法进行了重载,如果需要指定数组大小则可以在实例化顺序表类时输入指定的数组大小,如果没有指定数组的大小,我们就将数组大小默认设置为10个元素的大小!

那么成员属性和构造方法我们都实现了,接下来就是顺序表中的增删查改等方法了!

查找顺序表元素(indexOf)

我们查找元素一般时有两个需求。

第一:查找到元素返回元素的下标
第二:判断数组中有没有这个元素,如果有返回true否则返回false。

所以根据着两个需求我们可以写两个方法

第一个:当该元素存在时返回该元素的下标

  // 查找某个元素对应的位置
  //toFind是我们要查找的元素
    public int indexOf(E toFind) {
        for (int i = 0; i < usedSize; i++) {
            if (elem[i].equals(toFind)) {
                return i;
            }
        }
        return -1;
    }

第二个:当该元素存在时返回true否则返回false

 // 判定是否包含某个元素
    public boolean contains(E toFind) {
        for (int i = 0; i <usedSize ; i++) {
            if (elem[i].equals(toFind)) {
                return true;
            }
        }
        return false;
    }

两者的区别其实就是返回值的不同,其实实现起来是很简单的。

遍历顺序表(display)

那我们也可以接着这个思路再写一个打印方法,打印出数组所有元素的值

遍历数组有五种方法,虽然有些不会经常用到,但是小鱼还是一一给大家列举出来!
①、使用 for 循环打印

最简单的方法,但是需要自己去实现

  /**
     * 打印顺序表:
     * 根据usedSize判断即可
     */
    public void display() {
        for (int i = 0; i < usedSize; i++) {
            System.out.print(elem[i]+" ");
        }
        System.out.println();
    }

②、使用 Arrays.toString() 或 Arrays.deepToString()

对于一维数组,可以使用Arrays.toString()方法,它支持将任意类型的数组转换为字符串
一维数组用 Arrays.toString() 方法,多维数组用 Arrays.deepToString() 方法

 public void display() {
       //自己顺序表中不推荐使用 
        System.out.println(Arrays.toString(elem));
    }

:在自己实现的顺序表中不推荐使用Arrays.toString() 方法以及后面介绍到的遍历数组的方法,因为顺序表每次都是扩容2倍或者1.5倍的内存,这就说明我们的数组其实是有很多元素是没有被赋值默认为null或0的,又因为Arrays.toString() 方法会将数组中所以元素打印出来,那么就会有下面的情景
在这里插入图片描述

③、使用 Arrays.asList()

该方法是将数组转化为list
以下几点需要注意:
(1)该方法不适用于基本数据类型
byte,short,int,long,float,double,boolean),但可以用基本数据类型的包装类。比如int的包装类Integer.(Object 数组也是有效)
(2)该方法将数组与列表链接起来,当更新其中之一时,另一个自动更新
(3)不支持add和remove方法

 public void display() {
        //运行结果和上面情况一样!
        System.out.println(Arrays.asList(elem));
    }

运行结果
在这里插入图片描述

④、使用for each

for-each 是 for 循环的另外一种使用方式. 能够更方便的完成对数组的遍历. 可以避免循环条件和更新语句写错.

   public void display() {
        //依旧是打印全部数组
        for (E x:elem) {
            System.out.print(x+" ");
        }
        System.out.println();

    }

运行结果
在这里插入图片描述

⑤、使用迭代器

迭代器是设计模式的一种,后序容器接触多了再给大家铺垫(由于自己实现的顺序表没有迭代器,于是用的是java自带的)

public static void main(String[] args) {
     List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(666);
        list.add(888);
        Iterator<Integer> it = list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();
   }

运行结果

在这里插入图片描述

注意:

  1. ArrayList最长使用的遍历方式是:for循环+下标 以及 foreach
  2. 迭代器是设计模式的一种,后序容器接触多了再给大家铺垫

定义一个异常(SubscriptException)

由于我们的增,删,改以及获取下标对应得元素的值,都需要传下标这个参数,又因为我们输入的下标可能会越界,所以我们自己实现一个异常,一旦输入的下标越界,则抛出这个异常
注:

我们继承的是一个编译时异常也可以称之为受查异常的Exception,所以我们如果用到这个异常就需要声明这个异常!

class SubscriptException extends Exception {

    public SubscriptException(String e) {
        super(e);
    }
}

接下来我们就可以去实现后面经常会用到的判断下标是否会越界的方法了.

在这个方法中,当我们输入的下标的值如果比我们的数组的长度长或者说等于我们的数组的长度那么就抛出这个异常!

  public void transBoundary(int pos) throws SubscriptException {
       
        if (pos>=usedSize) {
            throw new  SubscriptException("输入下标异常");
        }
    }

那么有人抛出异常就一定有人去接收这个异常,在下面实现

   //检查要输入的位置是否合法
    private boolean checkPosInAdd(int pos) throws SubscriptException {
        try {
            transboundary(pos);
        }catch (SubscriptException e) {
            System.out.println("捕捉到了下标异常");
            e.printStackTrace();
            return false;
        }
        return true;//合法
    }

当我们捕捉到这个异常的时候返回false,否则返回true,注意我们在方法的参数后面都声明了这个异常!!!

增加元素(Add)

我们将铺垫整理好了之后就可以进行增,删,改等操作了!

我们增加元素有两种方式:

第一种是在数组的最后一个位置插入,也可以称为尾插!
第二种是指定位置插入。

我们先来讨论一下,插入元素需要注意什么?

1.注意下标是否越界
2.注意数组容量是否够用
3.在插入元素之后数组元素加一

第一种方式的实现(比较简单):

代码如下:

    // 新增元素,默认在数组最后新增
    public void add(E data) {
        if (isFull()) {
           elem= Capacity(elem);
        }
        elem[usedSize]=data;
        usedSize++;
    }

根据上面提到需要注意的第二点,我们就可以知道,该方法中的isFull方法就是判断数组容量是否够用,方法的实现如下

  /**
     * 判断当前的顺序表是不是满的!
     *
     * @return true:满   false代表空
     */
    public boolean isFull() {
    //当我的数组长度和我的元素个数一样多时,就返回true
    //意思就是需要扩容
        return usedSize==elem.length;
    }

如果数组容量不够,就去调用Capacity()方法,当然这个扩容数组的方法也是需要咱们自己实现的,方法具体内容如下

   //扩容数组
    public E[] Capacity(E[]array) {

        return Arrays.copyOf(array,array.length*2);
    }

我们的数组需要扩容时,一次扩容一倍,如果是扩容的容量太小,那么就需要扩容很多次,这就造成了时间上的浪费!虽然这样扩容可能会存在空间上的浪费,但是当我们学到线性表中的链表时就可以进一步的解决这个问题。

数组扩容之后,将数组进行尾插,最后数组元素个数加一!

第二种(指定位置插入):

这里需要考虑的就是,我们该如何插入,以及需要注意的事项.

小鱼先将总的代码拿出来,下面进行分析:

  public void add(int pos, E data) throws SubscriptException {
		//判断要插入的元素是否是最后一个位置
        if (pos==usedSize) {//如果是则调用尾插的方法
            add(data);
            return;
        }
       if (!checkPosInAdd(pos)) {
       //判断输入的下标是否越界,
       //如果越界则直接返回
           return;
       }
        //检查容量
        if (isFull()) {
            elem=Capacity(elem);
        }
        for (int i = usedSize-1; i >=pos; i--) {
            //向后覆盖
            elem[i+1]=elem[i];
        }
        set(pos,  data);
        usedSize++;
        System.out.println("添加成功");
    }

这一部分理解起来有一些困难,不过小鱼会画图一步一步剖析,大家如果看完之后还有疑问,或者改进的方法请大胆私信小鱼!!!
画图一步一步分析

在这里插入图片描述

第一步:我们先不管第一个方法,直接看第二个方法(蓝色框框)

在这里插入图片描述

但是其实是存在坑的,假如我要插入元素的位置是数组的最后一个位置,那么由于transBoundary()中的判定条件,当我输入的下标是数组的最后一个位置,又因为相等所以他就会抛出数组越界的异常,我们为了解决这个问题,我在执行这个方法前先进行了第一步的判断,也就是上图中绿色的框框里面,当我输入的下标就是数组的最后一个位置时,则直接调用尾插方法,避免了抛出异常。(解决方法很多,这只是小鱼认为好一些的)

第二步

接下来就是检查容量了,这个在上面讲到过这里就不再重复,直接跳到红框框里面的第四个方法。

前面讲到的一个小朋友上厕所的例子大家还记得吧,当在队伍中的小朋友去上厕所回来时,就需要各位小朋友将他之前的位置腾出来,那么他原来位置后的小朋友就需要集体后移,如图

在这里插入图片描述

所以我们可以根据该图的思路,自己实现一个插入方法!

    for (int i = usedSize-1; i >=pos; i--) {
            //向后覆盖
            elem[i+1]=elem[i];
        }
        set(pos,data);
        usedSize++;
        System.out.println("添加成功");

让我们的数组中所有在pos位置及pos位置后面的元素后移一位,移动之后将调用set()方法。

方法实现如下:

    // 给 pos 位置的元素设为【更新为】 value
    public void set(int pos, E value) throws SubscriptException {
        //修改元素
       if (!checkPosInAdd(pos)) {
           return;
       }
           elem[pos]=value;
    }

将pos位置的元素修改为要插入的元素,并且在使用这个方法时,也需要判断下标是否越界!
最后数组元素个数加一,打印添加成功提示程序员。

删除元素(Delete)

经过上述的增加元素,我们也可以发现,删除元素其实也是有两个方法:
方法一:删除最后一个元素
方法二:删除指定元素

方法一(尾删):

    public void delete() {
        //删除最后一个元素
        elem[usedSize-1]=null;
        usedSize--;
    }

由于我们的usedSize是数组元素的个数,又因为我们的数组是从0下标开始的,所以我们需要将usedSize-1,将该下标置为null之后再对数组元素个数进行减一。

方法二(指定删除):

这个问题就相当于前面讲到过的小朋友去厕所问题,当这个小朋友去厕所后,那么他后面的小朋友就需要向前走将他的位置补上,也就是都像前移,具体如何实现呢?

在这里插入图片描述

所以我们的大概思路就是,将要删除的元素的后面的元素都进行前移的操作,最后将最后一个元素置为null,最后数组元素个数减一.

注意:

需要判断下标是否越界

    public void delete(int pos) throws SubscriptException {
        //判断是否越界
        if (!checkPosInAdd(pos)) {
            return;
        }
        for (int i = pos; i < usedSize-1; i++) {
            elem[i]=elem[i+1];
        }
        //将最后一个元素置为null
        elem[usedSize-1]=null;
        //元素个数减一
        usedSize--;
        
    }

删除下标为2的元素,运行结果

在这里插入图片描述

还有一些小鱼就不进行讲解了,大家可以自己实现:

清空顺序表
获取顺序表长度
获取下标对于位置的元素
判断顺序表是否为空
删除第一次出现的关键字

总结

顺序表的缺点:

  • ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N)
  • 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

那么我们如何解决呢?

这些问题将会在链表中给大家讲解!!!

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎样翻译文本?这三种翻译方法我经常使用

大家是不是还在将收到的文本或资料一句一句地复制到浏览器去翻译&#xff0c;再将翻译结果粘贴回文内呢&#xff1f;这种方法固然可以&#xff0c;但要是遇到需要翻译的文本内容比较多的情况时&#xff0c;就会很浪费时间和精力&#xff0c;如果恰好被英语不好的小伙伴遇到这种…

NoSQL数据库之Redis2

Redis 事务 事务的基础概念 关于事务最常见的例子就是银行转账&#xff0c;A 账户给 B 账户转账一个亿 (T1)&#xff0c;买一块地盖房子。在这种交易的过程中&#xff0c;有几个问题值得思考&#xff1a; 如何同时保证上述交易中&#xff0c;A账户总金额减少一个亿&#xff…

[附源码]Python计算机毕业设计Django的毕业生就业系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

蜂鸟E203学习笔记(四)——取指

1.1 取值概述 1.1.1 如何快速取指 首先要保证存储器的读延时足够小&#xff0c;通常使用指令紧耦合存储器&#xff08;ITCM&#xff09;和指令缓存器&#xff08;ICache&#xff09;。 ITCM通常使用离处理核很近的SRAM因此实现极短的延时。但是没有过大的存储空间&#xff0…

社区系统项目复盘-2

文章目录登录模块注册登录账号设置登录模块 重要知识点&#xff1a; ThreadLocal&#xff0c;Hostholder采用Threadlocal持有用户信息&#xff0c;用于代替session对象 ThreadLocal采用线程隔离的方式存放数据&#xff0c;可以避免多线程之间出现数据访问冲突。ThreadLocal提…

小程序全局配置文件以及常用配置项

一、window常用配置 1.小程序根目录下的app.json文件时小程序的全局配置文件。常用配置项如下&#xff1a; ① pages 记录当前小程序所有页面的存放路径 ② window 全局设置小程序窗口的外观 ③ tabBar 设置小程序底部的tabBar效果 ④ style 是否启用新版的组件样式 …

【Spring项目中的Service理解】

目录 1. Spring项目中的核心组成部分 2. Spring项目中的Service 2.1 Service的功能作用 2.2 Service的实现 1. Spring项目中的核心组成部分 项目的核心组成部分图解&#xff1a; 2. Spring项目中的Service 2.1 Service的功能作用 Service是项目中用于处理业务逻辑的&#x…

【学习笔记66】JavaScript的深浅拷贝

一、赋值 只要是引用数据类型, 那么在赋值的时候, 就是引用地址的传递// 赋值:字符串const s1 123;let s2 s1; // 赋值console.log(s2 s1); // trues2 456;console.log(s1); // 123console.log(s2); // 456 let o1 { a: 1 };let o2 o1; // 赋值console.log…

【iOS】—— GET和POST以及AFNetworking框架

GET和POST以及AFNetworking框架 文章目录GET和POST以及AFNetworking框架GET和POSTGET和POST区别GETGET请求步骤GET请求代码POSTPOST请求步骤POST请求代码AFNetworking简介添加头文件GETGET方法GET方法参数GET方法代码样例POSTPOST方法第一种&#xff1a;第二种&#xff1a;先来…

[附源码]计算机毕业设计springboot防疫物资捐赠

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

springboot+jsp学生成绩查询考务系统

众所周知&#xff0c;现代信息技术是现代教育技术的基础和核心&#xff0c;培养和创新型的人才&#xff0c;必须依靠现代教育技术。从这一层意义上讲&#xff0c;我们说掌握一定的计算机应用技能已经成为国家未来的合格建设者的必备素质&#xff0c;所以现在在大学中对非计算机…

Huggingface的介绍,使用(CSDN最强Huggingface入门手册)

Huggingface的介绍&#xff0c;使用&#xff08;CSDN最强Huggingface入门手册&#xff09;返回论文和资料目录 1.Huggingface的简介 Huggingface即是网站名也是其公司名&#xff0c;随着transformer浪潮&#xff0c;Huggingface逐步收纳了众多最前沿的模型和数据集等有趣的工…

代码源每日一题div1 DP 数组划分

数组划分 - 题目 - Daimayuan Online Judge 题意&#xff1a; 思路&#xff1a; 关于位运算的最大值&#xff0c;只需要按位去贪心即可&#xff0c;即从高位向低位贪心&#xff0c;答案一定是1111000的形式 那么我们去枚举这个1和0的分界线在哪就好了 对于一个确定好的分界…

模板学堂丨DataEase用户操作日志分析大屏

DataEase开源数据可视化分析平台于2022年6月正式发布模板市场&#xff08;https://dataease.io/templates/&#xff09;。模板市场旨在为DataEase用户提供专业、美观、拿来即用的仪表板模板&#xff0c;方便用户根据自身的业务需求和使用场景选择对应的仪表板模板&#xff0c;并…

基于FPGA的ALU计算器verilog实现

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 Verilog HDL是一种硬件描述语言&#xff0c;以文本形式来描述数字系统硬件的结构和行为的语言&#xff0c;用它可以表示逻辑电路图、逻辑表达式…

粒子群算法查找函数最小值

实现 函数 F01、F06 的优化测试 以下内容是现有算法的运行结果、调参分析、及代码实现&#xff0c;用于给其他人提供参考&#xff0c;懒得改了hh 1. 运行结果 参数 w 0.5 &#xff08;可更改&#xff09; c1 2.0 &#xff08;可更改&#xff09; c2 2.0 &#xff08;可更改&…

2.每天进步一点点-Python爬虫需要了解一下基础的web相关内容

14天学习训练营导师课程&#xff1a; 杨鑫《Python 自学编程基础》 杨鑫《 Python 网络爬虫基础》 杨鑫《 Scrapy 爬虫框架实战和项目管理》 文章目录1.网络请求过程1.1通过 URL 查找服务器 IP1.2三次握手建立 TCP 连接1.3发送 HTTP 请求1.4服务器响应请求1.5浏览器解析 HTML1.…

21年-自研-笔试题

目录背景题目1、Object的常用方法2、 和 equals 的区别是什么&#xff1f;equals3、以下代码的运行结果4、以下代码的运行结果5、String, StringBuilder&#xff0c;StringBuffer6、ArrayList和LinkedList7、一些常用的线程安全的集合类8、以下代码的运行结果9、完成下面的代码…

Java环境准备——JDK下载和安装、IDEA下载和安装

一、JDK下载及安装 1、必要性&#xff1a;JDK是整个Java开发的核心。 2、下载网址&#xff1a;Java Downloads | Oracle 3、选择下载JDK17的原因&#xff1a;JDK17 是Java的长期支持版本。 4、下载到本地后&#xff0c;双击进行安装&#xff0c;然后点击下一步&#xff0c;安…

AI物品分类识别管理系统uniapp源码带文档教程

技术架构 技术框架&#xff1a;SpringBoot2 Mysql5.7 Mybatis-Plus uniapp Swagger2 RuoYi-fast swagger-bootstrap-ui 运行环境&#xff1a;jdk8 IntelliJ IDEA maven 宝塔面板 百度智能云平台服务 本地api接口端搭建教程 后端需要准备相关的IDE和JDK8开发环境 , 前…