手敲MyLinkedList,简单了解其运行逻辑

1.LinkedList的介绍和结构

        LinkedList的底层是双向链表结构,相对于之前的单向无头非循环链表来说,LinkedList最大的区别就是该链表可以增加了一条链接逻辑,可以从最后一个节点通过地址访问来到整个链表的头结点。

        通过以下集合框架,LinkedList也实现了List接口,具体如下:

        注意: 

        1. LinkedList 实现了 List 接口
        2. LinkedList 的底层使用了双向链表
        3. LinkedList 没有实现 RandomAccess 接口,因此 LinkedList 不支持随机访问
        4. LinkedList 的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
        5. LinkedList 比较适合任意位置插入的场景

        1.1 LinkedList的结构 

        双向无头非循环链表的节点是由三部分构成的,用来存储数据的value域,存放下一个节点地址的next域,以及用来存放前一个节点地址的prev域,其节点和链表结构如下图所示;

                   

        简单要点如下: 

                   

2.无头双向非循环链表的实现

2.1 自定义MyLinkedList类

        1、建立一个Ilist接口,在里面构造mylinkedlist链表要实现的抽象方法;

public interface IList {
    //头插法
    void addFirst(int data);
    //尾插法
    void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    boolean contains(int key);
    //删除第一次出现关键字为key的节点
    void remove(int key);
    //删除所有值为key的节点
    void removeAllKey(int key);
    //得到单链表的长度
    int size();
    void clear();
    void display();
}

        无头双向非循环链表的节点是由三个属性(value域、prev域和next域构成的),同时也要在自定义MyLinkedList类里面使用内部类创建链表节点类,之后在链表类里面创建一个头结点head来代表当前链表的引用,同时创建一个节点last来表示当前链表的最后一个节点;同时让该自定义类实现我们之前创建的接口,接下来重写接口里面的方法,让其能够具体化; 

public class MyLinkedList implements IList {
    static class ListNode {
        public int value;
        public ListNode prev;
        public ListNode next;

        public ListNode(int value) {
            this.value = value;
        }
    }

    public ListNode head;
    public ListNode last;

    @Override
    public void addFirst(int data) {

    }

    @Override
    public void addLast(int data) {

    }

    @Override
    public void addIndex(int index, int data) {

    }

    @Override
    public boolean contains(int key) {
        return false;
    }

    @Override
    public void remove(int key) {

    }

    @Override
    public void removeAllKey(int key) {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public void clear() {

    }

    @Override
    public void display() {

    }
}

2.2头插法 

        思路(图解如下):

        1、如果是空链表(头节点head为null),则要添加的节点node就是头节点,也是尾节点.

        2、头节点不为null:

        2.1 将原先head的前驱节点指向新增节点位置,新增节点后驱节点指向head节点的位置,注意和新节点建立连接一定要从前往后建立;

        

        2.2 head指向新增节点位置(新的链表的第一个节点为head)。

@Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            last = node;
        }else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

         执行结果在2.3中;

2.3 遍历链表

        思路:与mysinglelist链表的相同,这里略;代码如下

@Override
    public void display() {
        ListNode cur = head;
        while (cur != null){
            System.out.print(cur.value+"->");
            cur = cur.next;
        }
        System.out.println(" ");
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
//        myLinkedList.addFirst(2);
        myLinkedList.display();

    }

        执行结果如下:

        

2.4尾插法        

        思路:

          1、如果是空链表(头节点head为null),则要添加的节点node就是头节点,也是尾节点.

          2、头节点不为null:

          2.1 将原先last的后驱节点(last.prev)指向新增节点(node)位置,新增节点前驱节点指向last节点的位置,注意和新节点建立连接一定要从前往后建立

        2,2将node节点改为新的last节点;

        代码和测试结果如下:

@Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            last = node;
        }else {
            last.next = node;
            node.prev = last;
            last = node;
        }
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
    }

                    

2.5 链表长度

        对整个链表进行遍历,使用计数器进行记录遍历的次数,最后将计数器的值返回即可,下图代码是该方法的具体实现;

 @Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null){
            count++;
            cur=cur.next;
        }
        return count;
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
        System.out.println(myLinkedList.size());
    }

        测试结果如下:

        

2.6 任意位置插入

 思路:

        1、需要插入的位置必须为合法,如果不合法,我们会抛出一个异常进行提醒,所以首先自定义一个异常;

public class ListIndexOutOfException extends RuntimeException{
    public ListIndexOutOfException() {
    }
 
    public ListIndexOutOfException(String message) {
        super(message);
    }
}

         2、任意位置插入,首先分几种情况,插在开头,插在结尾,插在中间

        2.1 当插在链表开头和结尾时,可以使用头插法和尾差法

        2.2 如下图所示,按照z字形进行赋值和写代码;当插在其他的位置时,首先让cur走到index的位置(此处创建一个方法,找到index位置的节点并将这个节点定义为cur返回)(这时候就需要考虑将下一个节点加在index的位置时如何处理建立连接的顺序);其次注意建立连接的时候,一定要先建立原index前的节点和node节点添加节点的连接,其次再建立添加节点(node)和原index节点的连接,链表图解如下:

         具体方法代码如下:

@Override
    public void addIndex(int index, int data) {
        ListNode node = new ListNode(data);
        if(index < 0 || index > size()) {
            //抛自定义的异常
            throw new ListIndexOutOfException("你当前输入的索引有问题");
        }
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
        ListNode cur = findIndex(index);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }

    private ListNode findIndex(int index) {
        ListNode cur = head;
        while (index != 0){
            index--;
            cur = cur.next;
        }
        return cur;
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
        System.out.println(myLinkedList.size());
        myLinkedList.addIndex(3,99);
        myLinkedList.display();
    }

2.7 查找关键字

        对链表进行遍历,然后将关键字key和链表数值进行比较,如果存在key关键字则返回true;反之则返回false;

        方法具体实现的代码如下:

 @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    } 
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.addIndex(3,99);
        myLinkedList.display();
        System.out.println(myLinkedList.contains(1));
    }

         测试代码和执行结果如下:

2.8删除第一个关键字为key的节点

        思路:

        1、链表为空链表,不用操作;
        2、删除数据在第一个:首先让cur节点移动到第二个节点,其次判断新的链表是否只有一个节点

        2.1 如果只剩下cur这个节点,则让head指向null,让last指向null

        2.2让cur这个节点的prev指向null,其他的不变


        3、没有你要删除的数据,不用操作
        4、有你要删除的数据且不是第一个(cur节点是要删除的节点)

        4.1 删除数据最后一个:让last节点往前移一个单位

        4.2删除的数据不是最后一个:首先让cur的前一个节点的next域直接存cur下一个节点的地址,其次让cur下一个节点的prev域存放cur前一个节点的地址

        代码如下:

    @Override
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key){
                if(cur == head) {
                    //整个链表的头结点为要删除的节点
                    head = head.next;//head == null
                    if(head == null) {
                        last = null;
                    }else {
                        head.prev = null;
                    }
                }else {
                    //链表的其他节点是要删除的
                    cur.prev.next = cur.next;
                    if(cur.next == null) {
                        //要删除的是最后一个节点
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }else {
                cur =cur.next;
            }
        }
    }

2.9删除所有值为key的节点

        与删除第一次出现关键字为key的节点几乎是一模一样的;我们只需要遍历完整个链表,将return删掉就好。

        代码如下:

 @Override
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key){
                if(cur == head) {
                    //整个链表的头结点为要删除的节点
                    head = head.next;//head == null
                    if(head == null) {
                        last = null;
                    }else {
                        head.prev = null;
                    }
                }else {
                    //链表的其他节点是要删除的
                    cur.prev.next = cur.next;
                    if(cur.next == null) {
                        //要删除的是最后一个节点
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                }
            }
                cur =cur.next;
        }
    }

2.10清空链表

        1、只需要遍历整个链表,将每个节点的前驱与后继节点都置为null就好

    public void clear(){
        ListNode cur = head;
        while(cur != null) {
            cur.prev  = null;
            cur = cur.next;
            cur.prev.next = null;
        }
    }
 

         2、将链表的首节点和为节点指向null即可;

 public void clear() {
        head = null;
        last = null;
    }

 ps:本次内容就到这里了,如果你喜欢的话就请一键三连!!!

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

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

相关文章

【Java 基础】15 注解

文章目录 1.什么是注解2.元注解1&#xff09;定义2&#xff09;分类 3.内置注解4.自定义注解5.注解的基本语法6.验证注解是否生效7.注解的使用场景8.注解的注意事项结语 1.什么是注解 注解&#xff08;Annotation&#xff09;可以理解成一种特殊的 “注释” 注解定义时以 符号…

02.PostgreSQL 查询处理期间发生了什么?

PostgreSQL 查询处理期间发生了什么&#xff1f; 文中主要内容引用自PostgreSQL指南&#xff1a;内幕探索 查询处理是PostgreSQL中最为复杂的子系统。如PostgreSQL官方文档所述&#xff0c;PostgreSQL支持SQL2011标准中的大多数特性&#xff0c;查询处理子系统能够高效地处理这…

深度学习记录--梯度下降法

什么是梯度下降法&#xff1f; 梯度下降法是用来求解成本函数cost函数中使得J(w,b)函数值最小的参数(w,b) 梯度下降法的实现 通过对参数w,b的不断更新迭代&#xff0c;使J(w,b)的值趋于局部最小值或者全局最小值 如何进行更新&#xff1f; 以w为例&#xff1a;迭代公式 ww-…

Spring MVC学习随笔-控制器(Controller)开发详解:接受客户端(Client)请求参数

学习视频&#xff1a;孙哥说SpringMVC&#xff1a;结合Thymeleaf&#xff0c;重塑你的MVC世界&#xff01;&#xff5c;前所未有的Web开发探索之旅 第三章、SpringMVC控制器开发详解 3.1 核心要点 &#x1f4a1; 1. 接受客户端&#xff08;client&#xff09;请求参数[讲解] 2…

MySQL 临时数据空间不足导致SQL被killed 的问题与扩展

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;共1730人左右 1 2 3 4 5&#xff0…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(2)后端跨域、登录模块、springboot分层架构、IDEA修改快捷键、vue代码风格

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(2)后端跨域、登录模块、sp…

自然语言处理 (NLP) 中的组合语义分析

埃弗顿戈梅德&#xff08;Everton Gomede&#xff09; 一、介绍 自然语言处理 &#xff08;NLP&#xff09; 中的组合语义分析是一个引人入胜且复杂的话题。为了充分理解它&#xff0c;将这个概念分解成它的基本组成部分是至关重要的&#xff1a;组合语义及其在NLP中的应用。组…

【开源】基于JAVA的超市账单管理系统

项目编号&#xff1a; S 032 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S032&#xff0c;文末获取源码。} 项目编号&#xff1a;S032&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 总体设计3.2 前端设计3…

EasyExcel写入多个sheet

直接上代码&#xff1a; public static void main(String[] args) {// 设置excel工作簿ExcelWriter excelWriter EasyExcel.write("F:\\excel\\a.xls").build();List<User> userList new ArrayList<>();userList.add(new User("lisi", "…

Redis缓存的使用

什么是缓存 缓存就是数据交换的缓冲区&#xff0c;是存储数据的临时地方&#xff0c;一般读写性能较高。 缓存的作用&#xff1a; 降低后端负载提高读写效率&#xff0c;降低响应时间 缓存的成本&#xff1a; 数据一致性成本代码维护成本运维成本 Redis特点 键值型数据库…

STM32DAC输出可调电压、三角波、正弦波

STM32DAC输出可调电压、三角波、正弦波 DAC简介输出可调电压输出正弦波输出三角波 本期内容我们将学习stm32DAC的原理和使用方法 DAC简介 DAC&#xff0c;全称&#xff1a;Digital-to-Analog Converter&#xff0c;指数字/模拟转换器。可以将数字量转换为模拟量进行输出&#…

深入了解Vue.js:构建现代、响应式的前端应用

文章目录 1. Vue.js简介1.1 安装Vue.js 2. Vue的核心概念2.1 数据驱动2.2 组件化2.3 生命周期钩子 3. Vue的特性3.1 响应式数据3.2 模板语法3.3 组件通信 4. 示例项目结语 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1…

2023 如何下载最干净的 win10 win11 微软官方原版系统镜像(详细图文)

前言 不会吧不会吧&#xff0c;不会到现在还有人不会下载原版系统镜像吧 开始 win10官方下载工具下载地址&#xff1a;https://www.microsoft.com/zh-cn/software-download/windows10 win11官方下载工具下载地址&#xff1a;https://www.microsoft.com/zh-cn/software-downl…

java实验:数据库应用(idea+mysql+php)

设计用户注册和登录界面&#xff0c;实现用户注册和登录操作。 设计用户注册/登录界面;使用工具在MySQL中创建user表&#xff0c;包括学号、姓名、密码、专业、班级&#xff1b;实现注册操作&#xff1a;在user表中插入一条新纪录&#xff0c;但学号不能重复&#xff1b;实现登…

Mybatis 操作续集2(结合上文)

Mybatis 是一个持久层框架,用于简化数据库的操作,和Spring 没有任何关系,我们现在能使用它是因为 Spring Boot 把Mybatis 的依赖给引入进来了,在 pom.xml 里面 Mybatis 如何进行重命名? 看最后两行代码,这样就能重命名了 package com.example.mybatisdemo.mapper;import com…

最大单词数算法分析

题目描述&#xff1a; 算法一&#xff1a; 代码实现&#xff1a; # include<stdio.h> # include<string.h>int main(){//char text[100]"leet code";//char brokenLetters[26]"lt";char text[100]"hello world";char brokenLetters…

代码随想录第二十二天(一刷C语言)|组合总数电话号码的字母组合

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、组合总数 思路&#xff1a;参考carl文档和视频 1、需要一维数组path来存放符合条件的结果&#xff0c;二维数组result来存放结果集。 2、targetSum 目标和&#xff0c;也就是题目中的…

AD7124-4 实测热电偶数据读取,电压精度到稳定到±1uV, 电压波动260nV, 温度精度到±0.01℃

AD7124-4 实测热电偶数据读取&#xff0c;电压精度到稳定到1uV, 电压波动260nV, 温度精度到0.01℃ AD7124_STM32_ADI官网例程使用stm32 和ad7124做温控调试&#xff0c;发现效果还是不错的&#xff0c;至少比ads1256的效果好多啦&#xff01;Chapter1 AD7124-4 实测热电偶数据读…

OpenSSH 漏洞修复升级最新版本

Centos7系统ssh默认版本一般是OpenSSH7.4左右&#xff0c;低版本是有漏洞的而且是高危漏洞&#xff0c;在软件交付和安全扫描上是过不了关的&#xff0c;一般情况需要升级OpenSSH的最新版本 今天详细说下升级最新版本的处理过程&#xff08;认真看会发现操作很简单&#xff0c…

springboot 整合 Spring Security 上篇

1.创建springBoot 项目工程(spring6.0的底层、JDK17) 1.添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>配置完成启动访问controller会出现登录…
最新文章