数据结构(Java版)第九期:LinkedList与链表

专栏:数据结构(Java版)

个人主页:手握风云

目录

一、LinkedList的模拟实现

1.1. 头插法

1.2. 尾插法

1.3. 插入中间节点

1.4. 删除某个节点

1.5. 删除所有为key的元素

二、LinkedList的使用

2.1. 什么是LinkedList

2.2. LinkedList的使⽤

三、ArrayList和LinkedList的区别


一、LinkedList的模拟实现

        在前几期当中,我们实现的都是单向链表,这次我们要实现的是双向链表。双向链表每个节点包含三个域,分别是val,储存数据;prev储存上一个节点的地址;next,储存下一个节点的地址。(如下图所示)

        对于双向链表的实现,我们新建一个MyLinkedList类,新建一个接口IList,在类里面重写IList里面的方法。

public class MyLinkedList implements IList{
    static class ListNode{
        public int val;
        public ListNode prev;
        public ListNode head;

        public ListNode(int val) {
            this.val = val;
        }
    }
    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 display() {

    }

    @Override
    public void clear() {

    }
}
public interface IList {
    //头插法
    public void addFirst(int data);

    //尾插法
    public void addLast(int data);

    //任意位置插⼊第⼀个数据节点为0号下标
    public void addIndex(int index,int data);

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);

    //删除第⼀次出现关键字为key的节点
    public void remove(int key);

    //删除所有值为key的节点
    public void removeAllKey(int key);

    //得到单链表的⻓度
    public int size();

    public void display();

    public void clear();
}

1.1. 头插法

        对于实现尾插的方法其实很简单,把新的节点的next存储下一个节点的地址,同时旧的头节点的prev也要存储新的头节点的地址。

@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;
        }
    }

 @Override
    public void display() {
        ListNode cur = head;
        while(cur != null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
    }
public class Main {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(50);
        myLinkedList.addFirst(40);
        myLinkedList.addFirst(30);
        myLinkedList.addFirst(20);
        myLinkedList.addFirst(10);
        myLinkedList.addFirst(5);
        myLinkedList.display();
    }
}

    我们顺带也可以完成对链表长度和是否含有某个元素的实现

@Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while(cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }

public class Main {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addLast(10);
        myLinkedList.addLast(20);
        myLinkedList.addLast(30);
        myLinkedList.addLast(40);
        myLinkedList.addLast(50);
        myLinkedList.addLast(60);
        System.out.println(myLinkedList.size());

    }
}
@Override
    public boolean contains(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

1.2. 尾插法

      尾插法与头插法极其类似,这里不再过多叙述。

@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 class Main {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addLast(10);
        myLinkedList.addLast(20);
        myLinkedList.addLast(30);
        myLinkedList.addLast(40);
        myLinkedList.addLast(50);
        myLinkedList.addLast(60);
        myLinkedList.display();
    }
}

1.3. 插入中间节点

 

       如图所示,我们需要用到4个引用,大致过程可以通过4行代码来实现。先绑定后面,要注意修改每一个引用的时候,不能因为修改了前面而影响了后面。

node.next = cur;
cur.prev.next = node;
node.prev = cur.prev;
cur.prev = node;
@Override
    public void addIndex(int index, int data) {
        int len = size();
        if(index<0 || index>len){
            throw new RuntimeException("双向链表位置不合法:"+index);
        }
        if(index == 0){
            addFirst(data);
            return;
        }
        if(index == len){
            addLast(data);
        }
        ListNode node = new ListNode(data);
        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){
            cur = cur.next;
            index--;
        }
        return cur;
    }

1.4. 删除某个节点

    如果说,我们删除的只是中间的某个节点,我们只需要两行代码就能实现。

cur.prev.next = cur.next;
cur.next.prev = cur.prev;

       但是我们要删除的是头节点或者尾结点呢?我们可以先让head向前走一步,再把head.prev置为空,同理last节点也可以。

@Override
    public void remove(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                //删除头节点
                if(cur == head){
                    head = head.next;
                    head.prev = null;
                }else{
                    //删除尾结点
                    if(cur == last){
                        last = last.prev;
                        last.next = null;
                    }else{
                        //删除中间节点
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }else{
                cur = cur.next;
            }
        }
    }

        这么看我们的代码好像没问题,但我们需要考虑一个问题,如果说链表只有一个节点呢?就会抛出下图这样一个异常,我们只需要把last也手动置为空。

完整代码:

@Override
    public void remove(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                //删除头节点
                if(cur == head){
                    head = head.next;
                    if(head == null){
                        last = null;
                        return;
                    }
                    head.prev = null;
                }else{
                    //删除尾结点
                    if(cur == last){
                        last = last.prev;
                        last.next = null;
                    }else{
                        //删除中间节点
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }else{
                cur = cur.next;
            }
        }
    }

1.5. 删除所有为key的元素

        我们只需要对上一个方法进行一点改动就可以实现。我们让cur只遍历一遍链表,每次cur走完if语句之后直接去指向下一个节点。

@Override
    public void removeAllKey(int key) {
        ListNode cur = head;
        while(cur != null){
            if(cur.val == key){
                //删除头节点
                if(cur == head){
                    head = head.next;
                    if(head == null){
                        last = null;
                        return;
                    }
                    head.prev = null;
                }else{
                    //删除尾结点
                    if(cur == last){
                        last = last.prev;
                        last.next = null;
                    }else{
                        //删除中间节点
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
    }

二、LinkedList的使用

2.1. 什么是LinkedList

     从上面的图中可以看到LinkedList实现了众多的接口来帮助我们实现各种各样的功能,这里我们主要看List的接口。我们看一下LinkedList里面的源码

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

2.2. LinkedList的使⽤

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.addFirst(10);
        linkedList.addLast(20);
        System.out.println(linkedList);
    }
}

       我们通过LinkedList创建对象的时候,就可以直接调用以上方法。我们看一下addFirst和addLast的源码。

    public void addFirst(E e) {
        linkFirst(e);
    }
        public void add(E e) {
            checkForComodification();
            lastReturned = null;
            if (next == null)
                linkLast(e);
            else
                linkBefore(e, next);
            nextIndex++;
            expectedModCount++;
        }

    我们可以看到,代码的思路与我们上面自己实现的基本一致。我们同样可以初始化一个ArrayList。下面是一些常用方法:

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> list1 = new LinkedList<>();
        list1.add(1);//表示尾插
        list1.add(2);
        list1.add(3);
        list1.add(4);
        list1.add(5);
        list1.add(6);
        list1.add(3,100);//在下标为3的位置插入100
        System.out.println(list1);

        LinkedList<Integer> list2 = new LinkedList<>();
        list2.add(11);
        list2.add(22);
        list2.add(33);
        list1.addAll(2,list2);
        System.out.println(list1);

        list1.remove(2);//删除下标为2的元素
        list1.removeFirst();//删除头元素
        list1.removeLast();//删除尾元素
        System.out.println(list1);

        System.out.println(list1.get(2));//获取下标为2的元素
        list1.set(0,20);
        System.out.println(list1);//将下标为0的元素设置为20
    }
}

三、ArrayList和LinkedList的区别

不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(n)
头插需要搬移元素,效率低O(n)只需要改变指向,时间复杂度O(1)
插入空间不够时需要扩容没有容量的概念

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

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

相关文章

ubuntu18.04开发环境下samba服务器的搭建

嵌入式linux的发展很快&#xff0c;最近准备在一个新项目上采用新一代的linux核心板&#xff0c;发现linux内核的版本已经更新到5.4以上甚至6.0以上&#xff1b;之前常用的linux内核版本是2.6.4&#xff0c;虽然在某些项目上还能用但是明显跟不上时代的步伐了&#xff0c;所以要…

【优先算法】滑动窗口--(结合例题讲解解题思路)(C++)

目录 1. 例题1&#xff1a;最大连续1的个数 1.1 解题思路 1.2代码实现 1.3 错误示范如下&#xff1a;我最开始写了一种&#xff0c;但是解答错误&#xff0c;请看&#xff0c;给大家做个参考 2. 将 x 减到 0 的最小操作数 2.1解题思路 2.2代码实现 1. 例题1&#xff…

数据结构二叉树-C语言

数据结构二叉树-C语言 1.树1.1树的概念与结构1.2树的相关术语1.3树的表示1.4树形结构实际运用场景 2.二叉树2.1概念与结构2.2特殊的二叉树2.2.1满二叉树2.2.2完全二叉树 2.3二叉树存储结构2.3.1顺序结构2.3.2链式结构 3.实现顺序结构的二叉树4.实现链式结构二叉树4.1前中后序遍…

Qt/C++进程间通信:QSharedMemory 使用详解(附演示Demo)

在开发跨进程应用程序时&#xff0c;进程间通信&#xff08;IPC&#xff09;是一个关键问题。Qt 框架提供了多种 IPC 技术&#xff0c;其中 QSharedMemory 是一种高效的共享内存方式&#xff0c;可以实现多个进程之间快速交换数据。本文将详细讲解 QSharedMemory 的概念、用法及…

【vue3项目使用 animate动画效果】

vue3项目使用 animate动画效果 前言一、下载或安装npm 安装 二、引入组件三、复制使用四、完整使用演示总结 前言 提示&#xff1a;干货篇&#xff0c;不废话&#xff0c;点赞收藏&#xff0c;用到会后好找藕~ 点击这里&#xff0c;直接看官网哦 &#x1f449; 官网地址&#…

Android 15应用适配指南:所有应用的行为变更

Android系统版本适配&#xff0c;一直是影响App上架Google Play非常重要的因素。 当前Google Play政策规定 新应用和应用更新 必须以 Android 14&#xff08;API 级别 34&#xff09;为目标平台&#xff0c;才能提交到Google Play。现有应用 必须以 Android 13&#xff08;AP…

qml TargetDirection详解

1、概述 TargetDirection是QML&#xff08;Qt Modeling Language&#xff09;中一个用于指定粒子系统中粒子移动方向的类型。它允许粒子朝向一个目标点移动&#xff0c;这个目标点可以是QML界面上的一个具体位置&#xff0c;也可以是另一个QML元素的中心。TargetDirection通常…

Linux C 使用ZBar库解析二维码和条形码

1. 编译zbar库 下载 zbar 库源码&#xff0c;这里需要注意下&#xff0c;如果识别的二维码中有中文的话&#xff0c;会出现乱码&#xff0c;一般二维码里中文为UTF-8编码&#xff0c;zbar会默认给你把UTF-8转换为ISO8859-1。有两种解决办法&#xff0c;一是自己再转换一下编码…

金融项目实战 06|Python实现接口自动化——日志、实名认证和开户接口

目录 一、日志封装及应用&#xff08;理解&#xff09; 二、认证开户接口脚本编写 1、代码编写 1️⃣api目录 2️⃣script目录 2、BeautifulSoup库 1️⃣简介及例子 2️⃣提取html数据工具封装 3、认证开户参数化 一、日志封装及应用&#xff08;理解&#xff09; &…

基于springboot+vue+微信小程序的宠物领养系统

基于springbootvue微信小程序的宠物领养系统 一、介绍 本项目利用SpringBoot、Vue和微信小程序技术&#xff0c;构建了一个宠物领养系统。 本系统的设计分为两个层面&#xff0c;分别为管理层面与用户层面&#xff0c;也就是管理者与用户&#xff0c;管理权限与用户权限是不…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

【Vue】Vue组件--上

目录 一、组件基础 二、组件的嵌套关系 1. 基础架构 2. 嵌套 三、组件注册方式 1. 局部注册&#xff1a; 2. 全局注册&#xff1a; 四、组件传递数据 1. 基础架构 2. 传递多值 3. 动态传递数据 五、组件传递多种数据类型 1. Number 2. Array 3. Object 六、组…

鸿蒙UI开发——键盘弹出避让模式设置

1、概 述 我们在鸿蒙开发时&#xff0c;不免会遇到用户输入场景&#xff0c;当用户准备输入时&#xff0c;会涉及到输入法的弹出&#xff0c;我们的界面针对输入法的弹出有两种避让模式&#xff1a;上抬模式、压缩模式。 下面针对输入法的两种避让模式的设置做简单介绍。 2、…

python Streamlit和AKShare 实现的股票数据查询系统

1. 系统概述 这是一个基于Streamlit和AKShare的股票数据查询系统&#xff0c;提供了便捷的股票数据查询和可视化功能。系统支持按板块筛选股票、多股票代码查询、数据导出等功能。 1.1 主要功能 股票代码直接输入查询按板块筛选和选择股票历史数据和实时行情查询财务报表数据…

蓝桥杯备赛:顺序表和单链表相关算法题详解(上)

目录 一.询问学号&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; 2.解析与代码实现&#xff1a; &#xff08;1&#xff09;解析&#xff1a; &#xff08;2&#xff09;代码实现&#xff1a; 二.寄包柜&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; …

数据结构-ArrayLIst-一起探索顺序表的底层实现

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好&#xff0c;我们今天来学习java数据结构的第一章ArrayList&#xff08;顺序表&#xff09; 1.ArrayList的概念 那小伙伴就要问了线性表到…

RabbitMQ(四)

SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置&#xff1a; ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…

初始Java4

目录 一.继承 1.定义&#xff1a; 2.继承的语法&#xff1a; 3.子类访问父类 4.子类构造方法 5.super与this 6.继承方法 7.final关键字 &#xff08;1&#xff09;.变量不变 &#xff08;2&#xff09;.方法不变 &#xff08;3&#xff09;.类不可继承 8.继承与组合…

极限竞速 地平线5“d3dx12_43.dll”文件丢失或错误导致游戏运行异常如何解决?windows系统DLL文件修复方法

d3dx12_43.dll是存放在windows系统中的一个重要dll文件&#xff0c;缺少它可能会造成部分软件不能正常运行。当你的电脑弹出提示“无法找到d3dx12_43.dll”或“计算机缺少d3dx12_43.dll”等错误问题&#xff0c;请不用担心&#xff0c;我们将深入解析DLL文件错误的成因&#xf…

Leecode刷题C语言之超过阈值的最小操作数②

执行结果:通过 执行用时和内存消耗如下&#xff1a; // 最小堆的节点结构体 typedef struct {long long* heap;int size;int capacity; } MinHeap;// 初始化最小堆 MinHeap* createMinHeap(int capacity) {MinHeap* minHeap (MinHeap*)malloc(sizeof(MinHeap));minHeap->s…