数据结构(1):ArrayList和顺序表

news2024/9/16 9:54:13

数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。

下面我们就开一个新坑,数据结构。数据结构,简单来说就是存放数据的方式,这些方式多种多样,我们来一点一点认识。

首先就是最简单的顺序表。

1.顺序表的认识

线性表:n个具有相同特性的数据元素的有限序列

而常见的线性表就包含我们今天要认识的顺序表,同时还有列表、栈等等。

首先,它是一个序列,元素之间是有顺序的,若元素存在多个,则第一个无前驱,最后一个无后继,每个元素都有且只有一个前驱和后继。

顺序表是使用数组来完成的一种数据结构,顺序表的底层结构就是一个数组

我们java已经为我们写好了顺序表,并带了很多方法:

我们第一步要做的,就是自己实现这样一个数据结构,并将其中主要的方法,都一并实现。

2.顺序表的实现

显然,顺序表本身也是一个类,为了区分,我们可以给自己写的类取名MyArrayList。那么我们现在就可以创建这个类。

1)基础框架

我们刚才讲了,我们顺序表的底层逻辑是数组。因此我们这个类需要一个数组进行操作,用来存放数据元素。其次,我们需要获取数组当前已经使用的长度,这样方便我们操作,我们可以定义一个usedsize进行记录,初始值为0,最后,我们可以单独指定数组的容量,方便我们后续进行修改。综合以上,我们可以先写出这个类的框架:

public class MyArrayList {
    private int[] elem;//存放数据元素
    private int usedsize;
    private static final int initCapacity=10;
    public MyArrayList(){
        this.elem=new int[initCapacity];
    }
}

这样这个数据结构基本的结构就完成了,下面我们就要实现顺序表中自带的一些方法。具体有这些:

dispaly():打印所有数据

add():新增数据

contains():是否包含某个元素

indexof():查找某个元素位置

set():更新某位置的值

remove():删除第一次出现的关键字

size():获取顺序表的长度

clear():清空顺序表

那么我们可以先把这些方法构造出来,再谈具体实现:

public class MyArrayList {
    private int[] elem;//存放数据元素
    private int usedsize;
    private static final int initCapacity=10;
    public MyArrayList(){
        this.elem=new int[initCapacity];
    }
    public void add(int data) { }
    // 在 pos 位置新增元素
    public void add(int pos, int data) { }
    // 判定是否包含某个元素
    public boolean contains(int toFind) { return true; }
    // 查找某个元素对应的位置
    public int indexOf(int toFind) { return -1; }
    // 获取 pos 位置的元素
    public int get(int pos) { return -1; }
    // 给 pos 位置的元素设为 value
    public void set(int pos, int value) { }
    //删除第一次出现的关键字key
    public void remove(int toRemove) { }
    // 获取顺序表长度
    public int size() { return 0; }
    // 清空顺序表
    public void clear() { }
    //打印所有数据
    public void display(){}
}

2)方法的具体实现

下面我们来对每个方法都进行具体的实现。

display():

实质上就是对数组进行遍历

public void display(){
        //实质上就是遍历数组
        for (int i = 0; i < usedsize; i++) {
            System.out.println(this.elem[i]);
        }
    }

add():

要在数组最后进行新增

public void add(int data) { 
        this.elem[usedsize]=data;
        this.usedsize++;
        //考虑数组已满情况
    }

同时,我们还要考虑数组已满的情况。我们可以设置一个方法对是否已满进行判断,如果是,我们就进行扩容。那么,现在我们来设置一个isfull方法。

同时,我们,还要相应改变add方法,一旦数组满了,要进行扩容。

    public void add(int data) {
        this.elem[usedsize]=data;
        this.usedsize++;
        //考虑数组已满情况
        if(isfull()){
            this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
        }
    }
    public boolean isfull(){
        if (this.elem.length==this.usedsize){
            return true;
        }
        return false;
    }

这里,一旦数组已满,我们就扩容至两倍。当然,扩容我们也可以单独写成方法。

add(int pos,int data):

属于方法的重载,在pos位置新增元素。我们需要把pos后面的数据都往后挪一位,需要从最后一个开始挪,从后往前挪。要先考虑已满扩容问题,如果已满,要先扩容,再进行后续操作。同时我们也要注意pos的合法性,数据结构是一门严谨的学科。

 public void add(int pos, int data) {
        if(pos<0||pos>this.usedsize){
            System.out.println("位置不合法");
            return ;
        }
        if(isfull()){
            this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
        }
        for (int i = usedsize-1; i >=pos ; i++) {
            this.elem[i+1]=this.elem[i];
        }
        this.elem[pos]=data;
        this.usedsize++;
    }

contains():

找是否包含,同样是遍历数组,这个也很好理解。

 public boolean contains(int toFind) {
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toFind){
                return true;
            }
        }
        return false;
    }

注意这里是整形的比较因从我们使用==,如果是引用类型我们也不要忘了使用equals。

查找某个元素对应位置/查找某个位置对应元素

这个只需要注意pos的合法性就可以了,剩下的就是老生常谈的遍历数组。

public int indexOf(int toFind) {
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toFind){
                return i;
            }
        }
        System.out.println("该元素不存在");
        return -1;
    }
public int get(int pos) {
        if(pos<0||pos>this.usedsize){
            System.out.println("位置不合法");
            return -1;
        }
        return this.elem[pos];
    }

set():

更新操作,同样,判断是否合法。

 public void set(int pos, int value) {
        if(pos<0||pos>this.usedsize){
            System.out.println("pos不合法");
        }
        this.elem[pos]=value;
    }

size():

获取顺序表长度,这个就很简单了,直接返回usedsize就可以了

public int size() { return usedsize; }

remove():

删除第一次出现的关键字。遍历数组,找不到直接返回,找到了从后往前进行覆盖。

public void remove(int toRemove) {
        int pos=-1;
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toRemove){
                pos=i;
                break;
            }
        }
        if(pos==-1)
            return;
        for(int i=pos;i<this.usedsize-1;i++){
            this.elem[i]=this.elem[i+1];
        }
        this.usedsize--;
    }

clear():

清空数据,一般我们可以直接把usedsize置为0,这样就清空了。当我们表中存放的是地址时,我们就要一个个设置为null,防止内存泄漏。

public void clear() { 
        this.usedsize=0;
    }

那么我们就完成了自己的顺序表的实现。

public class MyArrayList {
    private int[] elem;//存放数据元素
    private int usedsize;
    private static final int initCapacity=10;
    public MyArrayList(){
        this.elem=new int[initCapacity];
    }
    public void add(int data) {
        this.elem[usedsize]=data;
        this.usedsize++;
        //考虑数组已满情况
        if(isfull()){
            this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
        }
    }
    public boolean isfull(){
        if (this.elem.length==this.usedsize){
            return true;
        }
        return false;
    }
    // 在 pos 位置新增元素
    public void add(int pos, int data) {
        if(pos<0||pos>this.usedsize){
            System.out.println("位置不合法");
            return ;
        }
        if(isfull()){
            this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
        }
        for (int i = usedsize-1; i >=pos ; i++) {
            this.elem[i+1]=this.elem[i];
        }
        this.elem[pos]=data;
        this.usedsize++;
    }
    // 判定是否包含某个元素
    public boolean contains(int toFind) {
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toFind){
                return true;
            }
        }
        return false;
    }
    // 查找某个元素对应的位置
    public int indexOf(int toFind) {
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toFind){
                return i;
            }
        }
        System.out.println("该元素不存在");
        return -1;
    }
    // 获取 pos 位置的元素
    public int get(int pos) {
        if(pos<0||pos>this.usedsize){
            System.out.println("pos不合法");
            return-1;
        }
        return this.elem[pos];
    }

    // 给 pos 位置的元素设为 value
    public void set(int pos, int value) {
        if(pos<0||pos>this.usedsize){
            System.out.println("pos不合法");
        }
        this.elem[pos]=value;
    }
    //删除第一次出现的关键字key
    public void remove(int toRemove) {
        int pos=-1;
        for (int i = 0; i < usedsize; i++) {
            if(this.elem[i]==toRemove){
                pos=i;
                break;
            }
        }
        if(pos==-1)
            return;
        for(int i=pos;i<this.usedsize-1;i++){
            this.elem[i]=this.elem[i+1];
        }
        this.usedsize--;
    }
    // 获取顺序表长度
    public int size() { return usedsize; }
    // 清空顺序表
    public void clear() {
        this.usedsize=0;
    }
    //打印所有数据
    public void display(){
        //实质上就是遍历数组
        for (int i = 0; i < usedsize; i++) {
            System.out.println(this.elem[i]);
        }
    }
}

以后用到顺序表,我们并不用自己实现,可以直接使用ArrayList这个类。

同时,我们可以双击shift->classes->ArrayList,来进行源码的查看。

下面我们来学习这个类的使用。

3.顺序表的使用

 public static void main(String[] args) {
        ArrayList<Integer> arrayList=new ArrayList<>();
    }

这就是我们最常用的无参构造方法,我们也可以在后面的小括号里添加容量。

当我们写出无参方法,并不分配内存,默认在第一次调用add时才会分配大小为10的内存,而扩容会按照1.5倍的方式进行扩容。

各种方法的使用,我们通过.进行调用,基本和我们自己写的没有区别。

我们要注意一个,就是remove。可以用来删除下标,也可以用来直接删除数据

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList=new ArrayList<>();
        arrayList.remove(2);//删除的是下标
        arrayList.remove(new Integer(2));//删除的是数据
    }
}

4.小结

这篇文章我们主要讨论了顺序表,一种由数组构造的数据结构,比较简单,希望大家可以认真掌握。

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

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

相关文章

Statcounter Global Stats 提供全球统计数据信息

Statcounter Global Stats 提供全球统计数据信息 1. Statcounter Global Stats2. Mobile & Tablet Android Version Market Share WorldwideReferences Statcounter Global Stats https://gs.statcounter.com/ Statcounter Global Stats are brought to you by Statcounte…

C++ 定时器

这是第一次独立设计一个模块&#xff0c;从接口定义&#xff0c;模块组合到多线程并发可能遇到的各种问题&#xff0c;虽然定时挺简单的&#xff0c;但是想设计精度高&#xff0c;并且能应对高并发似乎也不是很容易&#xff0c;当然&#xff0c;最后没有测试定时器的代码&#…

架构模式:MVC

引言 MVC&#xff0c;即 Model&#xff08;模型&#xff09;-View&#xff08;视图&#xff09;-Controller&#xff08;控制器&#xff09;&#xff0c;是广泛应用于交互式系统中的典型架构模式&#xff0c;尤其在 GUI 和 Web 应用中。 MVC 的概念源自 GOF&#xff08;Gang …

JS解密工具之**如何续期 Charles 的 SSL 证书**

本文由 jsjiami加密/一键JS解密 独家赞助 有问题请私聊加密官方客服 Charles 是一款常用的 HTTP 代理工具&#xff0c;用于调试网络请求。然而&#xff0c;Charles 的 SSL 证书会定期过期&#xff0c;如果 SSL 证书失效&#xff0c;你将无法对 HTTPS 请求进行抓包。本文将详细…

SQL语句中in条件超过1000怎么办?

博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 引言 当遇到SQL语句中IN条件超过1000个的情况时&#xff0c;可以采取以下几种策略来有效处理这一问题&#xff1a; 使用临时表&#xff1a;将IN列表中的值存储在临时表中&#xff0c;并将该临时表与查询表进行J…

【Python 千题 —— 算法篇】寻找最长回文子串

Python 千题持续更新中 …… 脑图地址 &#x1f449;&#xff1a;⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目背景 回文串是指一个字符串从左到右和从右到左读都是一样的。寻找一个字符串中的最长回文子串是许多经典算法问题之一&#xff0c;广泛应…

2024年9月最新界面:自己如何在电脑上注册新的Google谷歌账号,图文详解和关键点解析、常见问题

有一些朋友需要通过谷歌账号来工作、学习或娱乐&#xff08;例如很多游戏需要用谷歌账号来注册和使用&#xff09;&#xff0c;但是不知道如何注册谷歌账号&#xff0c;或者知道如何注册&#xff0c;但是对于一些步骤或者注意事项不太熟悉&#xff0c;导致注册不成功&#xff0…

什么是LED智能会议一体机?COB超微小间距LED会议一体机大势所趋

LED智能会议一体机&#xff0c;作为现代会议室革新的核心装备&#xff0c;正逐步颠覆传统会议模式的界限。它不仅仅是一台集成了高清显示、触控互动、音视频处理及远程协作等功能于一体的智能设备&#xff0c;更是推动会议效率与体验双重飞跃的关键力量。随着技术的不断进步&am…

【重学 MySQL】十八、逻辑运算符的使用

【重学 MySQL】十八、逻辑运算符的使用 AND运算符OR运算符NOT运算符异或运算符使用 XOR 关键字使用 BIT_XOR() 函数注意事项 注意事项 在MySQL中&#xff0c;逻辑运算符是构建复杂查询语句的重要工具&#xff0c;它们用于处理布尔类型的数据&#xff0c;进行逻辑判断和组合条件…

【Protobuf】初识protobuf以及详细安装教程

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 目录 序列化概念 ProtoBuf是什么 ProtoBuf在window下的安装 下载ProtoBuf编译器 配置环境变量 ​编辑 检查是否配置成功 ​编辑 ProtoBuf在Linux下的安装 下载ProtoBuf 安装ProtoBuf 序列化概念 首先我们…

小白开发中遇到的问题和解决方案

小白开发中遇到的问题和解决方案 文章目录 小白开发中遇到的问题和解决方案问题一 问题一 问题&#xff1a;端口别占用可能开开启多个应用 解决方法–在cmd执行下方红框中的命令关闭所有应用

MyBatis-MappedStatement什么时候生成?QueryWrapper如何做到动态生成了SQL?

通过XML配置的MappedStatement 这部分MappedStatement主要是由MybatisXMLMapperBuilder进行解析&#xff0c;核心逻辑如下&#xff1a; 通过注解配置的MappedStatement 核心逻辑就在这个里面了&#xff1a; 继承BaseMapper的MappedStatement 我们看看这个类&#xff0c;里…

idea如何配置模板

配置生成代码指令模板 注&#xff1a;我们常用的有sout,main等指令 第一步打开设置面板 1)按如下操作 2&#xff09;或者CtrlAltS快捷键直接弹出 第二步找 Editor>LiveTemplates 如下图 第三步创建模板 步骤如下 1&#xff09;创建分组名字 2)分组名字 3&#xff09;创…

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

pycharm如何安装selenium

在pycharm中打开一个项目后,点击Setting(ALTCtrlS快捷键) 然后点击install package完成后点击关闭这个窗口,就可以在代码中使用selenium了 成功后出现如下界面 编写一段正常可以运行操作chorme浏览器的 from selenium import webdriver # 指定ChromeDriver的路径driver we…

关于 PC打开“我的电脑”后有一些快捷如腾讯视频、百度网盘、夸克网盘、迅雷等各种捷方式在磁盘驱动器上面统一删除 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/142029325 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

淘宝开放平台交易类API解析以及如何测试?

调用淘宝开放平台的订单接口&#xff0c;主要可以通过以下几种途径进行&#xff1a; 1. 直接使用淘宝开放平台提供的API接口 步骤概述&#xff1a; 注册淘宝开放平台账号&#xff1a;首先&#xff0c;你需要在淘宝开放平台注册一个开发者账号。创建应用&#xff1a;在注册并…

Unity3D 小案例 像素贪吃蛇 01 蛇的移动

Unity3D 小案例 像素贪吃蛇 第一期 蛇的移动 像素贪吃蛇 今天来简单制作一个小案例&#xff0c;经典的像素贪吃蛇。 准备 首先调整一下相机的设置&#xff0c;这里使用灰色的纯色背景&#xff0c;正交视图。 接着&#xff0c;创建一个正方形&#xff0c;保存为预制体&#…

位运算技巧总结

一、常见位运算操作 1、基础位运算 & 按位与 有0则0 | 按位或 有1则1 ^ 按位异或 相同为0 不同为1 2、确定数n的二进制位中第x位是0还是1 目的&#xff1a;是0返回0&#xff0c;是1返回1 (n >> x) & 1 思路&#xff1a;1除了第一位其他位都是0&a…

01初识FreeRTOS【前情回顾篇】

为什么要使用FreeRTOS&#xff1f; 裸机轮询无法避免两个函数相互影响的问题&#xff0c;例如我们使用单片机在进行裸机开发时&#xff0c;我们使用了Delay延时函数&#xff0c;这时我们无法再执行其他的功能代码&#xff0c;需要等延时时间结束再执行其他代码&#xff0c;而使…