【数据结构】虽然很难很抽象,但是你还是得努力弄懂的数据结构——数组,你常用但是你懂它吗

news2025/1/9 1:34:39

数组(Array)

数组是实现顺序存储结构的基础,数组(Array)存储具有相同数据类型的元素集合.一维数组占用一块内存空间,数组的存储单元个数称为数组容量,也称为数组长度.

每个存储单元的地址是连续的,即每个元素连续存储,计算第i个元素地址所需时间是一个常量,时间复杂度是O(1),与元素序号i无关.存取任何一个元素的时间复杂度是O(1)的数据结构称为随机存取结构.因此,数据是数据存储结构.

数组通过下标识别元素,元素的下标是其存储单元序号,表示元素在数组中的位置.一维数组使用一个下标唯一确定一个元素.二维数组使用两个下标唯一确定一个元素.

数组一旦占用一片存储空间,其地址和容量就是确定的,不能更改.因此,数组只能进行赋值、取值两种随机存储操作,不能进行插入、删除操作。当数组容量不够时,不能就地扩容。

顺序表

线性表的顺序存储结构称为顺序表(Sequential List),它使用一维数组依次存放线性表从a0到an-1的数据元素(a0,a1,…,an-1),将ai(0≤i<n)存放在数组的第i个元素,使得ai与其前驱ai-1及后继ai+1的存储位置相邻,如图所示:
在这里插入图片描述

因此,数据元素在内存的物理存储次序反映了线性表数据元素之间的逻辑次序。

设Loc(a0)表示a0的存储地址,每个元素占用c个字节,则ai的存储地址Loc(ai)=Loc(a0)+i×c,是ai在线性表中序号i的线性函数,计算元素地址的时间复杂度是O(1)。因此,顺序表也是随机存取结构。

当顺序表使用的数组容量不够时,解决数据溢出的办法是,申请另一个更大容量的数组并进行数组元素复制,这样扩充了顺序表的容量。

接下来我们来看下顺序表类的具体实现;

顺序表的初始化是为顺序表分配一个预定义大小的数组空间,无参数构造方法设置顺序表长度为maxSize,有参数方法设置顺序表数组长度为形参n。初始化代码如下:

public class SequenceArrays<T> {
    // 默认初始值长度,2的幂
    final  int   DEFAULT_MAX_SIZE=1<<4;

    //存储元素的数组长度
    private  T[] listArray;

    // 保存顺序表的当前长度,即线性表当前有多少个数据
    private  int length;


    private  int arraySize;

    public SequenceArrays() {
        // 线性表初始为空,即还没有数据
        length=0;
        // 创建数组,数组长度为默认长度16,即还可以放入16个数据
        listArray  = (T[]) new Object[DEFAULT_MAX_SIZE];
    }


    public SequenceArrays(int arraySize) {
        //this.arraySize = arraySize;
        if (arraySize<0){
            throw new ArrayException("数组长度不能小于0");
        }
        // 线性表初始为空,即还没有数据
        length=0;
        // 创建数组,数组长度n,即还可以放入n个数据
        listArray  = (T[]) new Object[arraySize];
    }
    ...
}

顺序表的插入是指在顺序表的第pos-1个元素和第pos个元素之间插入一个新的元素,此时,顺序表中插入位置前后的元素之间的逻辑关系将发生变化,除非pos=n+1,否则必须通过顺序移动数据元素的存储位置才能体现逻辑关系的变化。插入过程如下图所示:
在这里插入图片描述

一般情况下,在第pos(1≤pos≤length)个元素之前插入一个元素时,需将第length至第pos(共length-pos+1)个元素向后移动一个位置。顺序表插入代码如下:

    /**
     *
     * @param obj 插入的元素 时间复杂度为O(n)
     * @param pos 插入元素的位置,数组元素的下标为pos-1
     * @return boolean true -插入成功, false- 插入失败
     */
    public  boolean add(T obj,int pos){
        //插入地址非法
        if ( pos<1 || pos>length+1){
            throw  new ArrayException("插入位置不合法");
        }
        //当前数组的位置已被数据填满
        if (length==listArray.length){
            //重新创建一个新的数组,数组长度为原长度的2倍
            T[] p = (T[]) new Object[length*2];
            //通过for循序把原数组的元素放入到新数组p中
            for (int i=0;i<length;i++){
                p[i]=listArray[i];
            }
            //把新数组p赋值给原listArray;
            listArray=p;
        }

        // 需要插入元素的位置的后续元素向后挪一个位置,从最后一个元素开始挪, 数组的下标=数组中元素个数-1.
        for (int i=length;i>=pos;i--){
            listArray[i]=listArray[i-1];
        }
        // 在指定位置插入元素obj
        listArray[pos-1]=obj;
        // 数组中元素个数加1
        length++;

        return  true;
    }

从源码可知,顺序表插入需要的注意的点如下:
(1)要检验插入位置的有效性,这里pos的有效范围是:1≤pos≤length+1,其中length为原表长。
(2)顺序表中数据区域有listArray.length个存储单元,所以在向顺序表中做插入时先检查表空间是否满了,在表满的情况下需要申请容量为原数组容量*2的数组并进行数组元素复制。
(3)注意数据的移动方向。从表尾开始依次向前,一个一个地往后移动数据元素,最后把要插入的元素obj放到数组下标为pos-1的地方。
(4)注意数据的移动方向。从表尾开始依次向前,一个一个地往后移动数据元素,最后把要插入的元素obj放到数组下标为pos-1的地方。

顺序表的删除是指删除顺序表中的第pos个元素,删除操作也会导致被删除位置前后的元素之间的逻辑关系发生变化,因此,除非删除位置是表中的最后一个元素,否则也必须通过顺序移动数据元素的存储位置才能体现逻辑关系上的变化。删除过程如下:
在这里插入图片描述

一般情况下,删除第pos(1≤pos≤length)个元素时需将从第pos+1至第length(共n-i)个元素依次向前移动一个位置,删除代码如下:

    /**
     * 移除数组中的元素  时间复杂度为O(n)
     * @param pos  需要移除的数组元素的位置,数组元素的下标为 pos-1
     * @return
     */
    public  T remove(int pos){
        //判断数组是否为空的
        if (isEmpty()){
            throw  new ArrayException("数组为空,不能执行删除操作");
        }else{
            if (pos<1 || pos>length){
                throw  new ArrayException("数组下标越界了,pos值不合法");
            }
            T t= listArray[pos-1];

            // 数组中的元素往前移动1外, 从下标为pos+1的元素开始移动
            for (int i= pos;i<=length;i++){
                listArray[i-1] =listArray[i];
            }
            //数组中元素个数需要减1
            length--;
            return  t;
        }
    }


从源码可知,顺序表的删除需要注意如下几个点:
(1)当表空时不能做删除。
(2)删除第pos个元素,pos的取值为1≤pos≤length,否则第pos个元素不存在,因此,要检查删除位置的有效性。
(3)注意数据移动的方向。把删除位置pos之后的数据依次前移一个位置,最后顺序表的长度减1。
(4)注意数据移动的方向。把删除位置pos之后的数据依次前移一个位置,最后顺序表的长度减1。

当在顺序表中某个位置上插入或删除一个数据元素时,其时间主要耗费在移动元素上。在顺序表中插入或删除一个数据元素,平均约移动表中一半元素,算法add和remove的时间复杂度均为O(n)。

顺序表的查找是指在顺序表中查找某个值等于给定值的元素位置。在顺序表中查找是否存在和obj相同的数据元素的最简便的方法是:把obj和顺序表中的数据元素逐个比较,若顺序表中存在和obj相同的元素,则比较次数为i(1≤i≤length),否则为length,即算法find的时间复杂度为O(n),则n=length。顺序表的查找代码如下:

/**
 * 查找元素 时间复杂度为O(n)
 * @param obj 需要查找的数组中的元素
 * @return 返回的是数组中的第几个元素, -1 表示没有查找到元素
 */
public  int find(T obj){
    if (isEmpty()){
        throw  new ArrayException("数组为空");
    }else{
        // 通过for循环,从下标0 开始元素查找,主要是通过元素值比对的方式查找
        for (int i=0;i<length;i++){
            listArray[i].equals(obj);
            return  i+1;
        }
    }
    return  -1;
}

顺序表中第pos个元素存放在数组listArray下标为pos-1的位置,也就是当位置转换为下标时要进行一个减一的运算。算法中1≤pos≤length,当pos值有效时返回listArray[pos-1],否则返回null,具体代码如下:

/**
 * 获取数组的第pos个位置的元素
 * @param pos 数组中元素的位置, 数组元素的下标为pos-1
 * @return
 */
public  T value(int pos){
    if (isEmpty()){
        throw  new ArrayException("数组为空");
    }else{
        if (pos<1 || pos>length){
            throw  new ArrayException("数组下标越界了,pos值不合法");
        }
        return  listArray[pos-1];
    }
}

则顺序表实现的完整代码如下:

public class SequenceArrays<T> {
    // 默认初始值长度,2的幂
    final  int   DEFAULT_MAX_SIZE=1<<4;

    //存储元素的数组长度
    private  T[] listArray;

    // 保存顺序表的当前长度,即线性表当前有多少个数据
    private  int length;


    private  int arraySize;

    public SequenceArrays() {
        // 线性表初始为空,即还没有数据
        length=0;
        // 创建数组,数组长度为默认长度16,即还可以放入16个数据
        listArray  = (T[]) new Object[DEFAULT_MAX_SIZE];
    }


    public SequenceArrays(int arraySize) {
        //this.arraySize = arraySize;
        if (arraySize<0){
            throw new ArrayException("数组长度不能小于0");
        }
        // 线性表初始为空,即还没有数据
        length=0;
        // 创建数组,数组长度n,即还可以放入n个数据
        listArray  = (T[]) new Object[arraySize];
    }

    /**
     *
     * @param obj 插入的元素 时间复杂度为O(n)
     * @param pos 插入元素的位置,数组元素的下标为pos-1
     * @return boolean true -插入成功, false- 插入失败
     */
    public  boolean add(T obj,int pos){
        //插入地址非法
        if ( pos<1 || pos>length+1){
            throw  new ArrayException("插入位置不合法");
        }
        //当前数组的位置已被数据填满
        if (length==listArray.length){
            //重新创建一个新的数组,数组长度为原长度的2倍
            T[] p = (T[]) new Object[length*2];
            //通过for循序把原数组的元素放入到新数组p中
            for (int i=0;i<length;i++){
                p[i]=listArray[i];
            }
            //把新数组p赋值给原listArray;
            listArray=p;
        }

        // 需要插入元素的位置的后续元素向后挪一个位置,从最后一个元素开始挪, 数组的下标=数组中元素个数-1.
        for (int i=length;i>=pos;i--){
            listArray[i]=listArray[i-1];
        }
        // 在指定位置插入元素obj
        listArray[pos-1]=obj;
        // 数组中元素个数加1
        length++;

        return  true;
    }

    /**
     * 移除数组中的元素  时间复杂度为O(n)
     * @param pos  需要移除的数组元素的位置,数组元素的下标为 pos-1
     * @return
     */
    public  T remove(int pos){
        //判断数组是否为空的
        if (isEmpty()){
            throw  new ArrayException("数组为空,不能执行删除操作");
        }else{
            if (pos<1 || pos>length){
                throw  new ArrayException("数组下标越界了,pos值不合法");
            }
            T t= listArray[pos-1];

            // 数组中的元素往前移动1外, 从下标为pos+1的元素开始移动
            for (int i= pos;i<=length;i++){
                listArray[i-1] =listArray[i];
            }
            //数组中元素个数需要减1
            length--;
            return  t;
        }
    }

    /**
     * 查找元素 时间复杂度为O(n)
     * @param obj 需要查找的数组中的元素
     * @return 返回的是数组中的第几个元素, -1 表示没有查找到元素
     */
    public  int find(T obj){
        if (isEmpty()){
            throw  new ArrayException("数组为空");
        }else{
            // 通过for循环,从下标0 开始元素查找,主要是通过元素值比对的方式查找
            for (int i=0;i<length;i++){
                listArray[i].equals(obj);
                return  i+1;
            }
        }
        return  -1;
    }

    /**
     * 获取数组的第pos个位置的元素
     * @param pos 数组中元素的位置, 数组元素的下标为pos-1
     * @return
     */
    public  T value(int pos){
        if (isEmpty()){
            throw  new ArrayException("数组为空");
        }else{
            if (pos<1 || pos>length){
                throw  new ArrayException("数组下标越界了,pos值不合法");
            }
            return  listArray[pos-1];
        }
    }

    /**
     * 修改数组第pos个位置的元素
     * @param obj 需要修改的值
     * @param pos 第pos个位置,即数组元素的下标为pos-1
     * @return
     */
    public  boolean modify(T obj,int pos){
        if (isEmpty()){
            throw  new ArrayException("数组为空");
        }else{
            if (pos<1 || pos>length){
                throw  new ArrayException("数组下标越界了,pos值不合法");
            }
            listArray[pos-1]=obj;
            return  true;
        }
    }

    /**
     * 获取素组的长度
     * @return
     */
    public  int size(){
        return  length;
    }

    /**
     * 打印数组元素
     */
    public void show(){
        System.out.println("");
        System.out.print("数组元素为:");
        int lastelement = length-1;

        // 打印所有数组元素
        for (int i=0;i<length;i++) {
            T element = listArray[i];
            if(element !=null){
                System.out.print(element);
                if (i != lastelement){
                    System.out.print("、");
                }
            }
        }
    }

    /**
     * 清空数组
     *
     */
    public  void clear(){
        length=0;
    }
    /**
     * 判断数组是否为空
     * @return
     */
    private boolean isEmpty() {
        boolean emptyflag =false;
        if(length==0 || listArray==null){
            emptyflag =  true;
        }
        return  emptyflag;
    }



    public static void main(String[] args) {
        int l,i;
        int[] a ={10,12,3,8,6,4,9};
        l=a.length;
        SequenceArrays<Integer>  arrays = new SequenceArrays<>();
        for (i=0;i<l;i++){
            arrays.add(a[i],i+1);
        }
        arrays.size();
        arrays.show();

        //插入元素
        arrays.add(17,5);
        arrays.size();
        arrays.show();

        //删除元素
        arrays.remove(5);
        arrays.size();
        arrays.show();

        //修改数组
        arrays.modify(90,6);
        arrays.size();
        arrays.show();
    }
}

在上述主函数中先初始化一个顺序表,再对该顺序表进行插入、删除、查找操作,程序运行结果如下:
在这里插入图片描述

顺序表具有的搜索方便,但插入或者删除某些元素就比较麻烦的特点。

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

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

相关文章

【论文解读|GL-Cache 】基于组级学习的缓存替换算法

论文原文&#xff1a; GL-Cache: Group-level learning for efficient and high-performance caching | FAST 23 源码 地址&#xff1a; GitHub - Thesys-lab/fast23-GLCache: Repository for FAST23 paper GL-Cache: Group-level Learning for Efficient and High-Performance…

基于Python+百度语音的智能语音ChatGPT聊天机器人(机器学习+深度学习+语义识别)含全部工程源码 适合个人二次开发

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Pycharm 环境ChatterBot 环境 模块实现1. 模型构建2. 服务器端3. 客户端4. 语音录入5. 接口调用6.模型训练及保存 系统测试1. 模型效果2. 模型应用 参考资料其它资料下载 前言 本项目基于机器学习和语义识别技术…

Qt翻金币小游戏详细教程(内涵所有源码、图片资源)

一、项目简介 翻金币项目是一款经典的益智类游戏&#xff0c;我们需要将金币都翻成同色&#xff0c;才视为胜利。首先&#xff0c;开始界面如下&#xff1a; 点击start按钮&#xff0c;进入下层界面&#xff0c;选择关卡&#xff1a; 在这里我们设立了20个关卡供玩家选择&…

IDEA使用技巧

1. 安装教程 1.1 安装过程 1.2 安装后的软件目录结构 目录结构&#xff1a; bin&#xff1a;容器&#xff0c;执行文件和启动参数等 这里以我的电脑系统(64 位 windows7&#xff0c; 16G 内存)为例&#xff0c;说明一下如何调整 VM 配置文件&#xff1a; 1、大家根据电脑系统…

【(Ubuntu22.04 Jammy)安装ROS 2 Iron Irwini】

ROS2 IronIrwini的Debian软件包目前可用于Ubuntu22.04 Jammy 1、安装ROS2 IronIrwini前准备 需要先安装好Ubuntu22.04 Jammy的前提下开始安装ROS2 1.1 Set locale 请确保Set locale支持UTF-8 locale # check for UTF-8sudo apt update && sudo apt install locale…

Rhapsody新手提示(1)如何在安装之后更换界面语言

DDD领域驱动设计批评文集>> 《软件方法》强化自测题集>> 《软件方法》各章合集>> 安装之后&#xff0c;如果还想更换界面语言&#xff0c;Windows平台操作如下&#xff1a; &#xff08;1&#xff09;在C:\ProgramData\IBM\Rhapsody\9.0.1x64文件夹下找到…

2023 年最新、最全、最实用的 Java 岗面试真题,已收录 GitHub

Java 面试 Java 作为编程语言中的 NO.1,选择入行做 IT 做编程开发的人&#xff0c;基本都把它作为首选语言,进大厂拿高薪也是大多数小伙伴们的梦想。以前 Java 岗位人才的空缺&#xff0c;而需求量又大&#xff0c;所以这种人才供不应求的现状&#xff0c;就是 Java 工程师的薪…

WIN32 API —— 最简单的Windows窗口封装类[通俗易懂]

1 开发语言抉择 1.1 关于开发Win32 程序的语言选择 C还是C 在决定抛弃MFC&#xff0c;而使用纯Win32 API 开发Window桌面程序之后&#xff0c;还存在一个语言的选择&#xff0c;这就是是否使用C。C作为C的超集&#xff0c;能实现所有C能实现的功能。其实反之亦然&#xff0c;…

机器学习 | matplotlib超详细教程

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

Git的安装和环境变量的配置

目录 前言一、下载Git二、安装Git三、检查是否安装成功四、 配置用户名和邮箱五、环境变量配置1. 获取git的安装路径2. 设置环境变量 前言 当我们第一次在新电脑上使用git命令的时候&#xff0c;会报错【git 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件】…

Typora+PicGo+阿里云OSS搭建博客图床

&#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司实习&#x1f…

中国民营快递:战事永不休

【潮汐商业评论/原创】 在中国&#xff0c;没有一个任何行业的“战争”在时间和烈度上可以与快递业相比。这是一场持续长达20年&#xff0c;融合了规模战、价格战、资本战等你能想象到的所有形态的立体化“战争”。 更令人难以置信的是&#xff0c;眼下这场以人力、技术为武器…

你还不会写系统?超详细驾校科考系统教程,手把手教学(内涵源码,Qt实现界面,有接口可供二次开发刷题使用)

目录 一、创建项目 二、登录界面 三、验证邮箱地址 四、验证账号密码 五、考试时间 六、初始化题库 七、布局按钮 八、提交试题 九、窗口交互 一、创建项目 新建Qt桌面应用程序&#xff0c;项目名&#xff1a;ExamSys。 类信息&#xff1a;类名LoginDialog继承自QDi…

Windows 7出现两个IP地址,导致联网问题

Qt源码解析 索引 Windows7 出现两个IP介绍 问题描述 win7电脑连接网线后出现两个IP地址&#xff0c;导致网络连接出现问题。 可能的现象有 连接网络出现黄色感叹号 局域网即时通信&#xff08;例如飞秋软件收发失败&#xff09; 修改IP地址不生效 服务软件启动报错&#…

12吋全自动划片机有哪些功能?

1、大面积工作盘&#xff1a;可容纳多个工件&#xff0c;并自动对位。 2、轴光/环光&#xff1a;采用合适的光源照射&#xff0c;显示影像更能呈现工作物表面特征。 3、双倍率显微镜头&#xff1a;视野更大&#xff0c;精准快速进行对准校正工作。 4、非接触测高&#xff1a…

Text2Video-Zero:Text-to-Image Diffusion Models are Zero-Shot Video Generators

【AIGC-AI视频生成系列-文章1】Text2Video-Zero - 知乎一句话亮点&#xff1a;当文本-视频生成也不需要额外数据训练&#xff0c;只需要基于现有的diffusion-model 如Stable Diffusion能力调整即可实现&#xff0c;解决生成视频帧间不一致问题&#xff0c;是不是很心动。 文章链…

腾讯云轻量2核2G4M带宽月流量/CPU/地域选择及限制说明

腾讯云轻量应用服务器2核2G4M活动上线了&#xff0c;这款轻量服务器4M公网带宽&#xff0c;每月300G免费流量&#xff0c;系统盘为50GB SSD盘&#xff0c;腾讯云百科来详细说下轻量应用服务器配置、租用费用、地域选择、CPU型号等详细说明&#xff1a; 目录 腾讯云轻量2核2G4…

2023年上半年软件设计师中级学习总结(超详细)

目录 前言 一、背景1.1上次考试感受&#xff1a;1.2这次考试感受&#xff1a;1.3方法&#xff1a; 二、 过程2.1计算机网络概论计算机组成数据表示相关知识校验码相关知识计算机体系结构网络体系结构OSI/RM和TCP/IP计算机安全性可靠性性能评价 2.2 程序设计语言基础知识编译和解…

前端开发如何更好的避免样式冲突?级联层(CSS@layer)

作者&#xff1a;vivo 互联网前端团队 - Zhang Jiqi 本文主要讲述了CSS中的级联层&#xff08;CSSlayer&#xff09;&#xff0c;讨论了级联以及级联层的创建、嵌套、排序和浏览器支持情况。级联层可以用于避免样式冲突&#xff0c;提高代码可读性和可维护性。 一、什么是级联…

选择交换机主要看哪些参数指标

交换机有几个性能指标您一定要知道哦&#xff0c;和海翎光电的小编一起温故而知新。 网络构成方式&#xff1a;接入层交换机、汇聚层交换机、核心层交换机 OST模型&#xff1a;第二层交换机、第三层交换机、第四层交换机……第七层交换机 交换机的可管理性&#xff1a;可管理…