数据结构-列表LinkedList

一,链表的简单的认识.

    数组,栈,队列是线性数据结构,但都算不上是动态数据结构,底层都是依托静态数组,但是链表是确实真正意义上的动态数组.

为什么要学习链表?

   1,链表时最简单的动态数据结构

   2,掌握链表有助于学习更复杂的数据结构,例如,二叉树,trie.

   3,学习链表有助于更深入的理解引用和递归,

1,链表

2,创建Node

二,链表的方法

1,对链表添加操作

 (1)链表头添加元素

(2)链表中间添加元素

关键点:找到要待添加位置的前一个结点
(3)向链表尾部添加元素

添加完成后

总结:

1,先创建节点node

2,找到最后一个节点pre

3,pre.next=node

2,使用虚拟头结点(解决在头部添加结点的特殊处理)

(注意:添加完成之后需要更新头节点)

3,链表的遍历,查询和更新操作

addHead() 向头部添加节点

addTail()  向尾部添加节点

add() 添加节点,默认头结点

get(index) 获取指定位置的节点

getFirst() 获取头节点

getLast() 获取尾节点

getSize() 获取索引

isEmpty() 判断链表是否为空

contains(val) 判断链表是否存在给定节点

toString() 遍历链表

4,从链表中删除元素

5,链表的时间复杂度分析

(1) 添加元素
(2) 删除操作
(4)修改操作
(5)查找操作

三,用代码实现链表

(需要注意的是用内部类,解决链表的数据类型(节点--Node))

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * 链表:真正的动态数据结构
 */
public class LinkedList<T> {
    // 定义结点
    private class Node {
        T val; // 结点的值
        Node next; // 表示下一个结点

        public Node(T val) {
            this.val = val;
        }

        public Node(T val, Node next) {
            this.val = val;
            this.next = next;
        }
    }


    // 链表的头结点
    private Node header;
    private int size;

    // 构造链表
    public LinkedList() {
        this.header = null;
        this.size = 0;
    }

    // 判断链表是否为空
    public boolean isEmpty() {
        return this.size == 0;
    }

    // 获取链表中元素的个数
    public int getSize() {
        return this.size;
    }

    // 向链表中添加元素

    /**
     * 在链表的头部添加
     */
    public void addHeader(T val) {
        add(0, val);
    }

    /**
     * 在链表的尾部添加
     */
    public void addTail(T val) {
        add(this.size, val);
    }

    // 在任意位置添加
    /*public void add(int index, T val) {
        if (index < 0 || index > this.size) {
            throw new IllegalArgumentException("index is invalid!");
        }

        // 1、创建结点
        Node node = new Node(val);
        // 特殊处理:头结点,为啥?头结点没有前驱
        if (index == 0) {
            this.header = node;
            this.size++;
            return;
        }
        // 2、找到插入位置的前驱pre
        Node pre = this.header;
        int count = 1;
        while (count < index) {
            pre = pre.next;
            count++;
        }
        // 3、改变索引的指向
        node.next = pre.next;
        pre.next = node;
        // 4、更新size
        this.size++;
    }*/

    public void add(int index, T val) {
        if (index < 0 || index > this.size) {
            throw new IllegalArgumentException("index is invalid!");
        }
        // 0、创建结点,作为头结点的前驱
        Node dummyHead = new Node(null);
        dummyHead.next = this.header;
        // 1、创建结点
        Node node = new Node(val);
        // 2、找前驱结点
        Node pre = dummyHead;
        int count = 1;
        while (count <= index) {
            pre = pre.next;
            count++;
        }
        // 3、改变索引的指向
        node.next = pre.next;
        pre.next = node;
        // 4、更新size
        this.size++;
        // 5、更新头结点
        this.header = dummyHead.next;
    }

    @Override
    public String toString() {
        List<String> result = new ArrayList<>();
        // 遍历链表
        Node cur = this.header;
        while (cur != null) {
            result.add(cur.val.toString() + "----->");
            cur = cur.next;
        }
        return result.stream().collect(Collectors.joining());
    }

    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 1; i <= 10; i++) {
            Random random = new Random();
            int val = random.nextInt(1000) + 1;
            System.out.println(val);
            linkedList.addHeader(val);
            System.out.println(linkedList);
        }
        linkedList.add(10,249);
        System.out.println(linkedList);
    }
}

四,使用链表实现队列

基本掌握链表的功能,就可以尝试着用链表实现栈或者队列,我们之前用的底层数据类型时数组.

// 定义节点类
class Node {
    int data;
    Node next;

    public Node(int data) {
        this.data = data;
        this.next = null;
    }
}

// 定义栈类
class Stack {
    private Node top;

    public Stack() {
        this.top = null;
    }

    public void push(int data) {
        Node newNode = new Node(data);
        if (top == null) {
            top = newNode;
        } else {
            newNode.next = top;
            top = newNode;
        }
    }

    public int pop() {
        if (top == null) {
            throw new EmptyStackException();
        } else {
            int data = top.data;
            top = top.next;
            return data;
        }
    }
}

// 定义队列类
class Queue {
    private Node front;
    private Node rear;

    public Queue() {
        this.front = null;
        this.rear = null;
    }

    public void enqueue(int data) {
        Node newNode = new Node(data);
        if (rear == null) {
            front = newNode;
            rear = newNode;
        } else {
            rear.next = newNode;
            rear = newNode;
        }
    }

    public int dequeue() {
        if (front == null) {
            throw new NoSuchElementException();
        } else {
            int data = front.data;
            front = front.next;
            if (front == null) {
                rear = null;
            }
            return data;
        }
    }
}

上面代码演示了如何用链表实现栈和队列,分别通过Stack类和Queue类来实现。可以根据自己的需要进一步扩展这两个类的功能,以满足具体的需求。

五,链表的用处

链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。

  1. 动态内存分配:链表允许在运行时动态地分配和释放内存,相比数组,不需要提前指定数据容量。

  2. 插入和删除节点:链表的插入和删除操作非常高效,只需要修改节点的指针即可,不需要移动其他元素。

  3. 不连续存储:链表的节点可以在内存中的任意位置,它们通过指针进行连接,可以灵活地利用内存空间。

  4. 可变长度:链表的长度可以根据需要进行动态调整,可以方便地增加或减少节点。

  5. 实现其他数据结构:链表可以作为其他数据结构的基础,例如栈、队列、哈希表等。

需要注意的是,链表在访问节点时需要遍历整个链表,因此随机访问效率较低,不适合频繁的随机访问操作。同时,链表需要额外的空间存储指针,因此相比数组会有一定的空间开销。根据具体问题的需求和场景,选择合适的数据结构非常重要。

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

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

相关文章

LeetCode 1038.从二叉搜索树到更大和树

给定一个二叉搜索树 root (BST)&#xff0c;请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。 提醒一下&#xff0c; 二叉搜索树 满足下列约束条件&#xff1a; 节点的左子树仅包含键 小于 节点键的节点。 节点的右子树仅包含键 大于 节点键的节点。 左…

js逆向-2

#md5加密&#xff0c;某宝案例演示。 #免责声明:本文仅供学习使用&#xff0c;请勿用于其他违法行为(╥ω╥)

软件性能测试和功能测试有何联系和区别?第三方软件检测机构简析

软件性能测试和功能测试是软件开发过程中非常重要的两个环节。从根本上说&#xff0c;它们都是为了保证软件质量和可靠性&#xff0c;但它们的目标和方法却有所不同。 软件性能测试是评估软件在特定负载下的性能表现&#xff0c;包括响应时间、吞吐量、并发能力等指标。它通过…

MySQL 学习记录 2

原文&#xff1a;https://blog.iyatt.com/?p13818 13 存储引擎 查看一下前面创建的一张表的创建语句&#xff0c;当时并没有显式指定引擎&#xff0c;MySQL 自动指定的 InnoDB&#xff0c;即默认引擎是这个。 创建表的时候要显式指定引擎可以参考这个语句 查看当前 MySQL …

如何正确使用Postman变量?又该如何灵活设置变量?

引言 Postman变量可以帮助你快速生成测试数据、模拟不同的场景和环境。 但是&#xff0c;如何正确使用Postman变量&#xff1f;又该如何灵活设置变量&#xff1f;这些问题不用担心&#xff0c;接着往下看吧&#xff01; 理解变量 为什么要使用变量&#xff1f; 如果在多个…

探索Java11新世界:JDK 11新特性详解

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

设计模式篇---观察者模式

文章目录 概念结构实例总结 概念 观察者模式&#xff1a;定义对象之间的一种一对多的依赖关系&#xff0c;使得每当一个对象状态发生改变时&#xff0c;其他相关依赖对象都得到通知并被自动更新。 观察者模式是使用频率较高的一个模式&#xff0c;它建立了对象与对象之间的依赖…

一文读懂列表解析、字典解析、集合解析

一、所谓解析/解析式&#xff0c;也称为推导/推导式&#xff0c;对应英语单词为comprehension&#xff0c;是Python的一种独有特性。解析就是从一个数据序列构建另一个新的数据序列的结构体&#xff0c;其本质是使用一个可迭代对象&#xff0c;按一定规则通过表达式、函数等运算…

Git的基本操作和原理

目录 写在前面的话 为什么要有Git&#xff08;git初识&#xff09;&#xff1f; Git安装(Centos为例) Git基本操作 创建Git本地仓库 Git配置 认识工作区、暂存区、版本库 概念认识 添加文件 查看.git文件 修改文件 版本回退 撤销修改 情况一&#xff1a;…

[数据集][目标检测]游泳者溺水数据集VOC+YOLO格式2类别895张

数据集制作单位&#xff1a;未来自主研究中心(FIRC) 数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;895 标注数量(xml文件个数)&#xff1a…

博途PLC PID仿真(单容水箱液位高度控制含变积分变增益测试)

单容水箱和双荣水箱的微分方程和数值求解,可以参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/131139432https://rxxw-control.blog.csdn.net/article/details/131139432这篇博客我们利用欧拉求解器在PLC里完成单容水箱的数学建模。PLC也可以和MATL…

SpringBoot Admin 详解

SpringBoot Admin 详解 一、Actuator 详解1.Actuator原生端点1.1 监控检查端点&#xff1a;health1.2 应用信息端点&#xff1a;info1.3 http调用记录端点&#xff1a;httptrace1.4 堆栈信息端点&#xff1a;heapdump1.5 线程信息端点&#xff1a;threaddump1.6 获取全量Bean的…

基于SSM的萌宠宜家商城系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的萌宠宜家商城系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring …

【黑马程序员】3、TypeScript常用类型_黑马程序员前端TypeScript教程,TypeScript零基础入门到实战全套教程

课程地址&#xff1a;【黑马程序员前端TypeScript教程&#xff0c;TypeScript零基础入门到实战全套教程】 https://www.bilibili.com/video/BV14Z4y1u7pi/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 3、TypeScript常用类型 3.1 类型注解 …

【51单片机】想学会串口通信,你需要知道这些(串口通信实验前置知识)(13)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

Qt Android sdk配置报错解决

使用的jdk8总是失败&#xff0c;报错command tools run以及platform sdk等问题。后来主要是设置jdk版本为17&#xff0c;就配置生效了。Android sdk路径可以选用Android Studio自带的&#xff0c;但是也要在Qt中点击“设置SDK”按钮做必要的下载更新等。 编译器这里会自动检测到…

【黑马程序员】2、TypeScript介绍_黑马程序员前端TypeScript教程,TypeScript零基础入门到实战全套教程

课程地址&#xff1a;【黑马程序员前端TypeScript教程&#xff0c;TypeScript零基础入门到实战全套教程】 https://www.bilibili.com/video/BV14Z4y1u7pi/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 2、TypeScript初体验 2.1 安装编译TS的工…

探究全链路压力测试的含义与重要性

全链路压力测试是指对整个应用系统的各个环节或组件进行压力测试&#xff0c;以模拟实际生产环境中的用户负载和流量&#xff0c;评估系统在高负载条件下的性能表现。 1. 全链路压力测试的含义 全链路压力测试涉及系统的所有组件和环节&#xff0c;包括前端用户界面、应用服务器…

算法沉淀——动态规划之路径问题(leetcode真题剖析)

算法沉淀——动态规划之路径问题 01.不同路径02.不同路径 II03.珠宝的最高价值04.下降路径最小和05.最小路径和06.地下城游戏 01.不同路径 题目链接&#xff1a;https://leetcode.cn/problems/unique-paths/ 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图…

c++: 用c++语言对车辆进行建模

一 原理 1.1 阿克曼转向模型 转向半径:后轴中心点到原点O的距离 已知道转向半径,可以反求转向角。或者知道转向角,可以求出转向半径。 四个顶点的转向半径。 还要定义这两个参数 1.2 车辆运动的建模 运动写在大的while循环里。 绘制车辆的思路;(1)清
最新文章