【数据结构】 LinkedList的模拟实现与使用

文章目录

  • 🍀什么是LinkedList
  • 🌴LinkedList的模拟实现
    • 🚩创建双链表
    • 🚩头插法
    • 🚩尾插法
    • 🚩任意位置插入
    • 🚩查找关键字
    • 🚩链表长度
    • 🚩打印链表
    • 🚩删除第一次出现关键字为key的节点
      • 📌删除的是头节点
      • 📌删除的是中间节点
      • 📌删除节点为尾节点
    • 🚩删除所有值为key的节点
    • 🚩清空链表
    • 🚩完整代码实现
  • 🎍LinkedList的使用
    • 🚩LinkedList的构造
    • 🚩LinkedList的其他常用方法介绍
    • 🚩LinkedList的遍历
  • 🎄ArrayList和LinkedList的区别
  • ⭕总结

🍀什么是LinkedList

LinkedList 的官方文档
LinkedList的底层是双向链表结构(链表后面介绍),由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。
在这里插入图片描述

🌴LinkedList的模拟实现

我们在这里创建一个MyLinkedList的java文件用于模拟实现我们的LinkedList。

🚩创建双链表

我们要模拟实现LinkendList,首先我们要有我们的双链表,这里我们来实现一个双链表

双链表所需要元素

  • 前驱节点:用于存储前一节点的位置,用prev表示
  • 后继节点:用于储存下一节点的位置,用next表示
  • 所需要储存的数据,用val表示
  • 头节点:用head表示
  • 尾节点:用last表示

如下图所示:
在这里插入图片描述
代码实现如下:

    static class ListNode {public int val;public ListNode prev;//前驱public ListNode next;//后继public ListNode(int val) {this.val = val;}}public ListNode head;//头节点public ListNode last;//尾节点

接下来我们将实现它的一些的功能

🚩头插法

在这里插入图片描述

实现思路:

  • 首先判断头节点是否为null
  • 若为null,则该节点就是头节点,也是尾节点
  • 则将原先head的前驱节点指向位置改为新增节点位置
  • 新增节点后驱节点指向位置改为head节点的位置
  • 将新增节点设为新的头节点

在这里插入图片描述

代码实现如下:

    //插法 O(1)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;}}

🚩尾插法

在这里插入图片描述
实现思路与头插法类似:

  • 首先判断头节点是否为null
  • 若为null,则该节点就是尾节点,也是头节点
  • 将last的后继节点设为node
  • node的前驱节点设为last
  • node设为新的尾节点

在这里插入图片描述
代码实现如下:

    //尾插法 O(1)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;}}

🚩任意位置插入

首先我们需要对插入数据进行判断其合法线性

这里我们自定义一个异常,为ListIndexOutOfException

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

其次我们可以对插入数据进行判断,若是在头尾进行插入,则可以直接用头插法与尾插法进行搞定

当所插入数据在中间时,我们首先要找到需要插入的节点,并返回,用cur接收

    private ListNode findIndex(int index) {ListNode cur = head;while (index != 0) {cur = cur.next;index--;}return cur;}

在这里插入图片描述

当我们找到要插入的节点后,我们的步骤为以下几步

  • 我们先将node的next置为cur;
  • 让cur前驱节点所指向的节点的后继节点指向node
  • 再将cur的前驱节点给node的前驱节点
  • 最后将cur的前驱节点置为node;
  • 在这里插入图片描述

代码实现如下:

    //任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int 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);//ListNode node = new ListNode(data);node.next = cur;cur.prev.next = node;node.prev = cur.prev;cur.prev = node;}

🚩查找关键字

直接遍历查找即可

代码实现如下

    //查找是否包含关键字key是否在单链表当中public boolean contains(int key){ListNode cur = head;while (cur != null) {if(cur.val == key) {return true;}cur = cur.next;}return false;}

🚩链表长度

用一个len变量进行记录,遍历链表,最后进行返回即可

代码实现如下:

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

🚩打印链表

遍历链表进行输出即可

代码实现如下:

    public void display(){ListNode cur = head;while (cur != null) {System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}

🚩删除第一次出现关键字为key的节点

我们大致的做法就是对链表进行遍历,遍历到相应的元素,删除即可,然后返回即可;

细分可分为三种情况

  • 删除的是头节点
  • 删除的是中间节点
  • 删除的是尾节点

📌删除的是头节点

我们只需要将head向前走一步就好

这时候又分为只一个节点和有多个节点的情况

当只有一个节点时,我们的head只需要向前走一步就好

如果有多个节点,我们还需要将新节点的前驱节点置为null;

代码实现如下:

 head = head.next;//只有一个节点if(head != null) {head.prev = null;}

📌删除的是中间节点

在这里插入图片描述

我们分为两步:

  • 将cur的前驱节点的后继节点变为cur的后继节点
  • 将cur的后继节点的前驱节点变为cur的前驱节点
  • 在这里插入图片描述

代码实现如下:

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

📌删除节点为尾节点

在这里插入图片描述

其实我们也分为两步:

  • 将cur的前驱节点的后继节点变为cur的后继节点
  • 将cur的前驱节点设为新的尾节点
  • 在这里插入图片描述

我们发现其实前面一步和删除中间节点一样,所以这里我们将他们组合起来,在删除中间节点里面加入判断即可

代码实现如下:

 //中间  尾巴cur.prev.next = cur.next;
//不是尾巴节点
if(cur.next != null) {cur.next.prev = cur.prev;
}else {
//是尾巴节点last = last.prev;
}

删除完后直接返回就好,如此一来,我们删除第一次出现关键字为key的节点完整代码也就可以出来了

代码如下:

    //删除第一次出现关键字为key的节点public void remove(int key){ListNode cur = head;while (cur != null) {//开始删除了if(cur.val == key) {//1. 删除的是头节点if(cur == head) {head = head.next;//只有一个节点if(head != null) {head.prev = null;}}else {//中间  尾巴cur.prev.next = cur.next;//不是尾巴节点if(cur.next != null) {cur.next.prev = cur.prev;}else {//是尾巴节点last = last.prev;}}return;}cur = cur.next;}}

🚩删除所有值为key的节点

这个代码实现起来与删除第一次出现关键字为key的节点几乎是一模一样的;

我们只需要return删掉就好

代码实现如下:

    //删除所有值为key的节点public void removeAllKey(int key){ListNode cur = head;while (cur != null) {//开始删除了if(cur.val == key) {//1. 删除的是头节点if(cur == head) {head = head.next;//只有一个节点if(head != null) {head.prev = null;}}else {//中间  尾巴cur.prev.next = cur.next;//不是尾巴节点if(cur.next != null) {cur.next.prev = cur.prev;}else {//是尾巴节点last = last.prev;}}}cur = cur.next;}}

🚩清空链表

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

代码实现如下:

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

🚩完整代码实现

public class MyLinkedList {static class ListNode {public int val;public ListNode prev;//前驱public ListNode next;//后继public ListNode(int val) {this.val = val;}}public ListNode head;//头节点public ListNode last;//尾节点//头插法 O(1)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;}}//尾插法 O(1)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;}}//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int 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);//ListNode node = new ListNode(data);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;}//查找是否包含关键字key是否在单链表当中public boolean contains(int key){ListNode cur = head;while (cur != null) {if(cur.val == key) {return true;}cur = cur.next;}return false;}//删除第一次出现关键字为key的节点public void remove(int key){ListNode cur = head;while (cur != null) {//开始删除了if(cur.val == key) {//1. 删除的是头节点if(cur == head) {head = head.next;//只有一个节点if(head != null) {head.prev = null;}}else {//中间  尾巴cur.prev.next = cur.next;//不是尾巴节点if(cur.next != null) {cur.next.prev = cur.prev;}else {//是尾巴节点last = last.prev;}}return;}cur = cur.next;}}//删除所有值为key的节点public void removeAllKey(int key){ListNode cur = head;while (cur != null) {//开始删除了if(cur.val == key) {//1. 删除的是头节点if(cur == head) {head = head.next;//只有一个节点if(head != null) {head.prev = null;}}else {//中间  尾巴cur.prev.next = cur.next;//不是尾巴节点if(cur.next != null) {cur.next.prev = cur.prev;}else {//是尾巴节点last = last.prev;}}}cur = cur.next;}}public int size(){int len = 0;ListNode cur = head;while (cur != null) {len++;cur = cur.next;}return len;}public void display(){ListNode cur = head;while (cur != null) {System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}public void clear(){ListNode cur = head;while(cur != null) {cur.prev  = null;cur = cur.next;cur.prev.next = null;}head = null;last = null;}
}

🎍LinkedList的使用

在集合框架中,LinkedList也实现了List接口,具体如下:
在这里插入图片描述

【说明】

  1. LinkedList实现了List接口

  2. LinkedList的底层使用了双向链表

  3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问

  4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)

  5. LinkedList比较适合任意位置插入的场景

🚩LinkedList的构造

在这里插入图片描述
构造代码如下:

import java.util.LinkedList;
import java.util.List;public class Main {public static void main(String[] args) {
// 构造一个空的LinkedListList<Integer> list1 = new LinkedList<>();List<String> list2 = new java.util.ArrayList<>();list2.add("JavaSE");list2.add("JavaWeb");list2.add("JavaEE");
// 使用ArrayList构造LinkedListList<String> list3 = new LinkedList<>(list2);}
}

🚩LinkedList的其他常用方法介绍

在这里插入图片描述
方法使用代码如下:

import java.util.LinkedList;
import java.util.List;public class Main {public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1); // add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());System.out.println(list);
// 在起始位置插入0list.add(0, 0); // add(index, elem): 在index位置插入元素elemSystem.out.println(list);list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()list.removeFirst(); // removeFirst(): 删除第一个元素list.removeLast(); // removeLast(): 删除最后元素list.remove(1); // remove(index): 删除index位置的元素System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回falseif(!list.contains(1)){list.add(0, 1);}list.add(1);System.out.println(list);System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置int elem = list.get(0); // get(index): 获取指定位置元素list.set(0, 100); // set(index, elem): 将index位置的元素设置为elemSystem.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回List<Integer> copy = list.subList(0, 3);System.out.println(list);System.out.println(copy);list.clear(); // 将list中元素清空System.out.println(list.size());}
}

🚩LinkedList的遍历

    public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1); // add(elem): 表示尾插list.add(2);list.add(3);list.add(4);list.add(5);list.add(6);list.add(7);System.out.println(list.size());
// foreach遍历for (int e:list) {System.out.print(e + " ");}System.out.println();
// 使用迭代器遍历---正向遍历ListIterator<Integer> it = list.listIterator();while(it.hasNext()){System.out.print(it.next()+ " ");}System.out.println();
// 使用反向迭代器---反向遍历ListIterator<Integer> rit = list.listIterator(list.size());while (rit.hasPrevious()){System.out.print(rit.previous() +" ");}System.out.println();}

🎄ArrayList和LinkedList的区别

在这里插入图片描述

⭕总结

关于《【数据结构】 LinkedList的模拟实现与使用》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

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

相关文章

javaScript:带你深入了解基本数据类型和引用类型

目录 一.前言 二.Ecmascript 规定的变量有两种 补充 1.基本数据类型&#xff08;重点掌握&#xff09; 基本数据类型的特点 2.引用数据类型 &#xff08;重点掌握&#xff09; 引用数据类型的特点 三.什么是栈&#xff1f;堆&#xff1f; 下面代码帮助了解 解释 官…

spring boot分装通用的查询+分页接口

背景 在用spring bootmybatis plus实现增删改查的时候&#xff0c;总是免不了各种模糊查询和分页的查询。每个数据表设计一个模糊分页&#xff0c;这样代码就造成了冗余&#xff0c;且对自身的技能提升没有帮助。那么有没有办法实现一个通用的增删改查的方法呢&#xff1f;今天…

爬虫工具的选择与使用:阐述Python爬虫优劣势

作为专业爬虫ip方案解决服务商&#xff0c;我们每天都面对着大量的数据采集任务需求。在众多的爬虫工具中&#xff0c;Python爬虫凭借其灵活性和功能强大而备受青睐。本文将为大家分享Python爬虫在市场上的优势与劣势&#xff0c;帮助你在爬虫业务中脱颖而出。 一、优势篇 灵活…

MySQL的基础操作

前言 对MySQL的一些基础操作做一下学习性的总结&#xff0c;基本上是照着视频写的。 MySQL的安装 MySQL的下载 MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/ 配置环境变量 下载之后直接解压&#xff0c…

c++ 虚函数类对象模型

一、复杂的菱形继承及菱形虚拟继承 单继承&#xff1a;一个子类只有一个直接父类时称这个继承关系为单继承。 多继承&#xff1a;一个子类有两个或以上直接父类时称这个继承关系为多继承。 菱形继承&#xff1a;菱形继承是多继承的一种特殊情况。 菱形继承的问题&#xff1a;从…

2023年京东儿童智能手表行业数据分析(京东销售数据分析)

儿童消费市场向来火爆&#xff0c;儿童智能手表作为能够实现定位导航&#xff0c;信息通讯&#xff0c;SOS求救&#xff0c;远程监听&#xff0c;智能防丢等多功能的智能可穿戴设备&#xff0c;能够通过较为精准的定位功能和安全防护能力保障儿童的安全&#xff0c;因而广受消费…

C#详解-Contains、StartsWith、EndsWith、Indexof、lastdexof

目录 简介: 过程: 举例1.1 举例1.2 ​ 总结: 简介: 在C#中Contains、StarsWith和EndWith、IndexOf都是字符串函数。 1.Contains函数用于判断一个字符串是否包含指定的子字符串&#xff0c;返回一个布尔值&#xff08;True或False&#xff09;。 2.StartsWith函数用于判断一…

数据结构-二叉树

在学习二叉树之前.必须先要掌握一些树的重要概念: 结点的度:一个结点含有的子树个数称为该结点的度.树的度:一棵树中,所有节点度的最大值称为树的度.叶子结点:度为0的结点称为叶子节点.(也叫终端结点)双亲结点:若一个结点含有子结点,则这个结点称为其子结点的双亲结点(也叫父节…

USB隔离器电路分析,SA8338矽塔sytatek电机驱动,源特科技VPS8701,开关电源,电源 大师

一、 USB隔离器电路分析 进行usb隔离可以使用USB隔离模块 ADUM3160 ADUM4160 注意&#xff1a;B0505S 最大带载0.16A&#xff0c;副边需要带载能力需要改变方案 比如移动硬盘至少需要0.5A 用充电宝、18650、设计5V1A输出电源 二、 1A隔离电压方案

redis乐观锁+启用事务解决超卖

乐观锁用于监视库存&#xff08;watch&#xff09;&#xff0c;然后接下来就启用事务。 启用事务&#xff0c;将减库存、下单这两个步骤&#xff0c;放到一个事务当中即可解决秒杀问题、防止超卖。 但是&#xff01;&#xff01;&#xff01;乐观锁&#xff0c;会带来" …

Leetcode67 二进制求和

给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 代码 class Solution {public String addBinary(String a, String b) {StringBuilder res new StringBuilder();int carry 0;int i a.length() - 1, j b.length() - 1;while(i > 0 || j &…

keepalived+lvs+nginx高并发集群

keepalivedlvsnginx高并发集群 简介&#xff1a; keepalivedlvsnginx高并发集群&#xff0c;是通过LVS将请求流量均匀分发给nginx集群&#xff0c;而当单机nginx出现状态异常或宕机时&#xff0c;keepalived会主动切换并将不健康nginx下线&#xff0c;维持集群稳定高可用 1.L…

Linux系统之安装OneNav个人书签管理器

Linux系统之安装OneNav个人书签管理器 一、OneNav介绍1.OneNav简介2.OneNav特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本3.3 检查本地yum仓库状态 四、安装httpd服务4.1 安装httpd4.2 启动httpd服务4…

低代码开发ERP:精打细算,聚焦核心投入

企业数字化转型已经成为现代商业环境中的一项关键任务。如今&#xff0c;企业面临着日益激烈的竞争和不断变化的市场需求。在这样的背景下&#xff0c;数字化转型不仅是企业生存的必然选择&#xff0c;也是取得竞争优势和实现可持续发展的关键因素。 在数字化转型的过程中&…

神经网络入门

前言 本文主要介绍最基础的神经网络&#xff0c;包括其结构&#xff0c;学习方法&#xff0c; C \texttt{C} C 的实现代码。 Python \texttt{Python} Python 的代码可以搜索互联网得到。 前排提示&#xff1a;本人涉及一丁点数学知识。 神经网络的结构 神经网络包括多个层…

[Linux]进程概念

[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例&#xff0c;是担当分配系统资源&#xff08;CPU时间&#xff0c;内存…

二、数学建模之整数规划篇

1.定义 2.例题 3.使用软件及解题 一、定义 1.整数规划&#xff08;Integer Programming&#xff0c;简称IP&#xff09;&#xff1a;是一种数学优化问题&#xff0c;它是线性规划&#xff08;Linear Programming&#xff0c;简称LP&#xff09;的一个扩展形式。在线性规划中&…

构造不包含字母和数字的webshell

文章目录 利用不含字母与数字进行绕过知识介绍题目方法一&#xff1a;异或操作绕过方法二&#xff1a;取反进行绕过 过滤不是很严格的情况进阶绕过利用php7特性直接绕过 利用不含字母与数字进行绕过 知识介绍 <?phpecho "A"^"";?>从运行结果为! …

深度学习|CNN卷积神经网络

CNN卷积神经网络 解决的问题人类的视觉原理原理卷积层——提取特征池化层——数据降维全连接层——输出结果 应用图像处理自然语言处理 解决的问题 在CNN没有出现前&#xff0c;图像对人工智能来说非常难处理。 主要原因&#xff1a; 图像要处理的数据量太大了。图像由像素组…

使用IDEA把Java程序打包成jar

点击左上角File,选择Project Structure 左侧选中Artifacts,点击右侧的号 选择JAR->From modules with dependencies 选择你要运行的main方法所在的类,选好了点击OK Artifacts添加完成后点击右下角OK 在工具栏中找到Build,选择Build Artifacts 刚才创建好的Artifacts,选择Bui…