从零开始学java--线性表

news2025/4/16 14:10:20

数据结构基础

目录

数据结构基础

线性表

顺序表

链表

顺序表和链表的区别:

队列 


线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表中的元素个数就是线性表的长度,表的起始位置称为表头,结束位置称为表尾,当一个线性表中没有元素时称为空表。

线性表是一种在实际中广泛使用的数据结 构,常见的线性表:顺序表、链表、栈、队列...

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。


顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

我们存放数据还是使用数组,但是为其编写一些额外的操作来强化为线性表,像这样底层依然采用顺序存储实现的线性表称为顺序表:

public class ArrayList <E>{  //泛型E,因为表中要存的具体数据类型待定
    private int size=0;  //当前已经存放的元素数量
    private int capacity=10;  //当前顺序表的容量
    private Object[]array=new Object[capacity];  //底层存放的数组
}

当插入元素时需要将插入位置腾出来,也就是将后面所有元素向后移;

如果要删除元素,需要将所有元素向前移动。

顺序表是紧凑的,不能出现空位。

插入方法(把元素放入顺序表):

public class ArrayList <E>{
    private int size=0;
    private int capacity=10;
    private Object[]array=new Object[capacity];

    public void add(E element,int index){  //插入方法需要支持在指定下标位置插入
        if(index<0||index>size)
            throw new IndexOutOfBoundsException("插入位置非法,合法的插入位置为:0-"+size);
        //在插入前判断,允许插入的位置只有[0,size]
        for (int i = size; i >index ; i--)  //从后往前一个一个搬运元素
            array[i]=array[i-1];
        array[index]=element;  //腾出位置后插入元素放到对应的位置上
        size++;  //插入完成后将size自增(扩大)
    }

//重写toString方法打印当前存放的元素
    public String toString(){
        StringBuilder builder=new StringBuilder();
        for (int i = 0; i < size; i++) builder.append(array[i]).append(" ");
        return builder.toString();
    }
}
public class Main {
    public static void main(String[] args) {
        ArrayList<String>list=new ArrayList<>();
        list.add("aaa",0);
        list.add("bbb",1);
        System.out.println(list);
    }
}
//输出aaa bbb

扩容操作:

    public void add(E element,int index){
        if(index<0||index>size)
            throw new IndexOutOfBoundsException("插入位置非法,合法的插入位置为:0-"+size);
        if(size>=capacity) {
            int newCapacity=capacity+(capacity>>1);  //原本容量的1.5倍
            Object[]newArray=new Object[newCapacity];  //创建一个新数组来存放更多的元素
            System.arraycopy(array,0,newArray,0,size);  //使用arraycopy快递拷贝原数组内容到新的数组
            array=newArray;  //更换为新的数组
            capacity=newCapacity;  //容量变成扩容之后的
        }

删除操作:

可删除的范围只可能是[0,size)

    @SuppressWarnings("unchecked")  //屏蔽未经检查警告
    public E remove(int index){  //删除对应位置上的元素,注意需要返回被删除的元素
        if(index<0||index>size-1)
            throw new IndexOutOfBoundsException("删除位置非法,合法的删除位置为:0-"+(size-1));
        E e=(E)array[index];  //因为存放的是Object类型,需要强制类型转换为E
        for (int i = index; i <size ; i++)  //从前往后,挨个往前搬一位
            array[i]=array[i+1];
        size--;
        return e;
    }

获取指定下标位置上的元素:

    @SuppressWarnings("unchecked")
    public E get(int index){
        if(index<0||index>size-1)
            throw new IndexOutOfBoundsException("查询位置非法,合法位置为:0"+(size-1));
        return (E) array[index];
    }

判断某位置是否为空:

    public boolean isEmpty(){
        return size==0;
    }


System.out.println(list.get(2));


链表

顺序表的缺陷:

ArrayList底层使用数组来存储元素:由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java 集合中又引入了LinkedList,即链表结构。

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。在逻辑结构上连续,但在物理上不一定连续。

实际中链表的结构非常多样:

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环

无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。这种结构在笔试面试中出现很多。

无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

带头单向链表:

public class LinkedList <E>{
    
    //链表的头结点,用于连接之后的所有结点
    private final Node<E>head=new Node<>(null);
    private int size; //存当前元素数量,方便后续操作
    
    private static class Node<E>{  //结点类,仅供内部使用
        private E element;  //每个结点都存放元素
        private Node<E> next;  //以及指向下一个结点的引用
        
        public Node(E e){
            this.element=e;
        }
    }
}

链表的插入:

    public void add(E element,int index){
        if(index<0||index>size)
            throw new IndexOutOfBoundsException("插入位置非法,合法的插入位置为:0-"+size);
        //先找到对应位置的前驱结点
        Node<E>prev=head;  
        for (int i = 0; i < index; i++) 
            prev =prev.next;

        Node<E>node=new Node<>(element);  //创建新的结点
        node.next=prev.next;  //新的结点指向原本在这个位置上的结点
        prev.next=node;  //让前驱结点指向当前结点
        size++;  //更新size
    }

toString方法:

    @Override
    public String toString(){
        StringBuilder builder=new StringBuilder();
        Node<E> node=head.next; //从第一个结点开始,一个一个遍历,遍历一个就拼接到字符串上去
        while (node!=null){
            builder.append(node.element).append(" ");
            node=node.next;
        }
        return builder.toString();
    }

删除操作:

    public E remove(int index){
        if(index<0||index>size-1)
            throw new IndexOutOfBoundsException("删除位置非法,合法的删除位置为:0-"+(size-1));
        Node<E>prev=head;
        for (int i = 0; i < index; i++) //找到前驱结点
            prev=prev.next;
        E e=prev.next.element;  //先把待删除结点存放的元素取出来
        prev.next=prev.next.next;
        size--;
        return e;
    }

获取对应位置上的元素:  (有点蒙,做个标记)

    public E get(int index){
        if(index<0||index>size-1)
            throw new IndexOutOfBoundsException("查询位置非法,合法位置为:0"+(size-1));
        Node<E>node=head;
        while (index-->=0)  //这里直接让index减到-1为止
            node=node.next;
        return node.element;
    }

    public int size(){
        return size;
    }


顺序表和链表的区别:

不同点 ArrayList LinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
插入空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁


栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据在栈顶。

 栈可以使用顺序表实现,也可以使用链表实现,使用链表会更加方便。我们可以直接将头结点指向栈顶结点,而栈顶结点连接后续的栈内结点。

入栈操作:

当有新的元素入栈,只需要在链表头部插入新的结点即可。

public class LinkedStack <E>{
    private final Node<E> head=new Node<>(null);
    
    public void push(E element){
        Node<E>node=new Node<>(element);  //直接创建新结点
        node.next=head.next;  //新结点的下一个变成原本的栈顶结点
        head.next=node;  //头结点的下一个改成新的结点
    }
    
    public boolean isEmpty(){
        return head.next==null;
    }

    private static class Node<E>{
        private E element;
        private Node<E> next;

        public Node(E e){
            this.element=e;
        }
    }
}

出栈操作:

    public E pop(){
        if(isEmpty()){ //如果栈没有元素了无法取
            throw new NoSuchElementException("栈为空");
        }
        E e=head.next.element;  //先把待出栈元素取出来
        head.next=head.next.next;  //直接让头结点的下一个指向下一个的下一个
        return e;
    }
    public static void main(String[] args) {
        LinkedStack<String>stack=new LinkedStack<>();
        stack.push("A");
        stack.push("B");
        stack.push("C");
        stack.push("D");
        while (!stack.isEmpty()){
            System.out.println(stack.pop());
        }
    }
//输出
D
C
B
A


队列 

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)

入队列:进行插入操作的一端称为队尾(Tail/Rear)

出队列:进行删除操作的一端称为队头 (Head/Front)

当有新的元素入队时,只需要拼在队尾就可以了,同时队尾指针也要后移一位。

出队时,只需要移除队首指向的下一个元素即可。

public class LinkedQuene <E>{

    private final Node<E> head=new Node<>(null);
    
    //入队操作
    public void offer(E element){
        Node<E>tail=head;
        while (tail.next!=null) //入队直接放到最后一个结点的后面
            tail=tail.next;
        tail.next=new Node<>(element);
    }
    
    //出队操作
    public E poil(){
        if(isEmpty())  //若队列没有元素了无法取出
            throw new NoSuchElementException("队列为空");
        E e=head.next.element;
        head.next=head.next.next;  //直接从队首取出
        return e;
    }
    
    //仅获取不移除队首
    public E peak(){
        if(isEmpty())
            throw new NoSuchElementException("队列为空");
        return head.next.element;
    }
    

    public boolean isEmpty(){
        return head.next==null;
    }

    private static class Node<E>{
        private E element;
        private Node<E> next;

        public Node(E e){
            this.element=e;
        }
    }
}

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

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

相关文章

AD917X系列JESD204B MODE7使用

MODE7特殊在F8&#xff0c;M4使用2个复数通道 CH0_NCO10MHz CH1_NCO30MHZ DP_NCO50MHz DDS1偏移20MHz DDS2偏移40MHz

Spring Cloud之远程调用OpenFeign最佳实践

目录 OpenFeign最佳实践 问题引入 Feign 继承方式 创建Module 引入依赖 编写接口 打Jar包 服务提供方 服务消费方 启动服务并访问 Feign 抽取方式 创建Module 引入依赖 编写接口 打Jar包 服务消费方 启动服务并访问 服务部署 修改pom.xml文件 观察Nacos控制…

【Python爬虫】详细入门指南

目录 一、简单介绍 二、详细工作流程以及组成部分 三、 简单案例实现 一、简单介绍 在当今数字化信息飞速发展的时代&#xff0c;数据的获取与分析变得愈发重要&#xff0c;而网络爬虫技术作为一种能够从互联网海量信息中自动抓取所需数据的有效手段&#xff0c;正逐渐走入…

Win11系统 VMware虚拟机 安装教程

Win11系统 VMware虚拟机 安装教程 一、介绍 Windows 11是由微软公司&#xff08;Microsoft&#xff09;开发的操作系统&#xff0c;应用于计算机和平板电脑等设备 。于2021年6月24日发布 &#xff0c;2021年10月5日发行 。 Windows 11提供了许多创新功能&#xff0c;增加了新…

打造AI应用基础设施:Milvus向量数据库部署与运维

目录 打造AI应用基础设施&#xff1a;Milvus向量数据库部署与运维1. Milvus介绍1.1 什么是向量数据库&#xff1f;1.2 Milvus主要特点 2. Milvus部署方案对比2.1 Milvus Lite2.2 Milvus Standalone2.3 Milvus Distributed2.4 部署方案对比表 3. Milvus部署操作命令实战3.1 Milv…

【深度学习与大模型基础】第11章-Bernoulli分布,Multinoulli分布

一、Bernoulli分布 1. 基本概念 想象你抛一枚硬币&#xff1a; 正面朝上&#xff08;记为 1&#xff09;概率是 p&#xff08;比如 0.6&#xff09;。 反面朝上&#xff08;记为 0&#xff09;概率是 1-p&#xff08;比如 0.4&#xff09;。 这就是一个Bernoulli分布&…

基于Windows通过nginx代理访问Oracle数据库

基于Windows通过nginx代理访问Oracle数据库 环境说明&#xff1a; 生产环境是一套一主一备的ADG架构服务器&#xff0c;用户需要访问生产数据&#xff0c;基于安全考虑&#xff0c;生产IP地址不能直接对外服务&#xff0c;所以需要在DMZ部署一个前置机&#xff0c;并在该前置机…

北斗和GPS信号频率重叠-兼容与互操作

越来越多的同学们发现北斗三代信号的B1C&#xff0c;B2a信号居然和美国GPS L1,L5处在同样频率上&#xff1f; 为什么美国会允许这样的事情发生&#xff1f;同频率难道不干扰彼此的信号吗&#xff1f; 思博伦卫星导航技术支持文章TED 这事得从2006年联合国成立全球卫星导航系统…

python爬虫:喜马拉雅案例(破解sign值)

声明&#xff1a; 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 根据上一篇文章&#xff0c;我们破解了本网站的&#xff0c;手机号和密码验证&#x…

51单片机波特率与溢出率的关系

1. 波特率与溢出率的基本关系 波特率(Baud Rate)表示串口通信中每秒传输的位数(bps),而溢出率是定时器每秒溢出的次数。在51单片机中,波特率通常通过定时器的溢出率来生成。 公式关系: 波特率=溢出率/​分频系数 其中,分频系数与定时器的工作模…

摄影测量——单像空间后方交会

空间后方交会的求解是一个非线性问题&#xff0c;通常采用最小二乘法进行迭代解算。下面我将详细介绍具体的求解步骤&#xff1a; 1. 基本公式&#xff08;共线条件方程&#xff09; 共线条件方程是后方交会的基础&#xff1a; 复制 x - x₀ -f * [m₁₁(X-Xₛ) m₁₂(Y-…

基于RV1126开发板的人脸姿态估计算法开发

1. 人脸姿态估计简介 人脸姿态估计是通过对一张人脸图像进行分析&#xff0c;获得脸部朝向的角度信息。姿态估计是多姿态问题中较为关键的步骤。一般可以用旋转矩阵、旋转向量、四元数或欧拉角表示。人脸的姿态变化通常包括上下俯仰(pitch)、左右旋转(yaw)以及平面内角度旋转(r…

鲲鹏+昇腾部署集群管理软件GPUStack,两台服务器搭建双节点集群【实战详细踩坑篇】

前期说明 配置&#xff1a;2台鲲鹏32C2 2Atlas300I duo&#xff0c;之前看网上文档&#xff0c;目前GPUstack只支持910B芯片&#xff0c;想尝试一下能不能310P也部署试试&#xff0c;毕竟华为的集群软件要收费。 系统&#xff1a;openEuler22.03-LTS 驱动&#xff1a;24.1.rc…

机器学习中 提到的张量是什么?

在机器学习中, 张量(Tensor) 是一个核心数学概念,用于表示和操作多维数据。以下是关于张量的详细解析: 一、数学定义与本质 张量在数学和物理学中的定义具有多重视角: 多维数组视角 传统数学和物理学中,张量被定义为多维数组,其分量在坐标变换时遵循协变或逆变规则。例…

edge 更新到135后,Clash 打开后,正常网页也会自动跳转

发现了一个有意思的问题&#xff1a;edge 更新135后&#xff0c;以前正常使用的clash出现了打开deepseek也会自动跳转&#xff1a; Search Resultshttps://zurefy.com/zu1.php#gsc.tab0&gsc.qdeepseek &#xff0c;也就是不需要梯子的网站打不开了&#xff0c;需要的一直正…

prime 1 靶场笔记(渗透测试)

环境说明&#xff1a; 靶机prime1和kali都使用的是NAT模式&#xff0c;网段在192.168.144.0/24。 Download (Mirror): https://download.vulnhub.com/prime/Prime_Series_Level-1.rar 一.信息收集 1.主机探测&#xff1a; 使用nmap进行全面扫描扫描&#xff0c;找到目标地址及…

第16届蓝桥杯单片机模拟试题Ⅲ

试题 代码 sys.h #ifndef __SYS_H__ #define __SYS_H__#include <STC15F2K60S2.H> //sys.c extern unsigned char UI; //界面标志(0湿度界面、1参数界面、2时间界面) extern unsigned char time; //时间间隔(1s~10S) extern bit ssflag; //启动/停止标志…

打造现代数据基础架构:MinIO对象存储完全指南

目录 打造现代数据基础架构&#xff1a;MinIO对象存储完全指南1. MinIO介绍1.1 什么是对象存储&#xff1f;1.2 MinIO核心特点1.3 MinIO使用场景 2. MinIO部署方案对比2.1 单节点单驱动器(SNSD/Standalone)2.2 单节点多驱动器(SNMD/Standalone Multi-Drive)2.3 多节点多驱动器(…

OOM问题排查和解决

问题 java.lang.OutOfMemoryError: Java heap space 排查 排查手段 jmap命令 jmap -dump,formatb,file<file-path> <pid> 比如 jmap -dump:formatb,file./heap.hprof 44532 使用JVisualVM工具&#xff1a; JVisualVM是一个图形界面工具&#xff0c;它可以帮…

「出海匠」借助CloudPilot AI实现AWS降本60%,支撑AI电商高速增长

&#x1f50e;公司简介 「出海匠」&#xff08;chuhaijiang.com&#xff09;是「数绘星云」公司打造的社交内容电商服务平台&#xff0c;专注于为跨境生态参与者提供数据支持与智能化工作流。平台基于大数据与 AI 技术&#xff0c;帮助商家精准分析市场趋势、优化运营策略&…