【数据结构——链表的深度探索】从实现到应用,保姆级攻略

【数据结构——链表深度探索】从实现到应用,保姆级攻略

  • 🍁1. 链表的介绍
  • 🍁2. 链表的实现
    • 🍁2.1 单向链表
      • 🍁2.1.1 size()
      • 🍁2.1.2 display()
      • 🍁2.1.3 contains(int key)
      • 🍁2.1.4 addFirst(int key),addLast(int key),addIndex(int key, int index)
      • 🍁2.1.5 remove(int key),removeAllKey(int key)
      • 🍁2.1.6 clear()
    • 🍁2.2 双向链表
      • 🍁2.2.1 addFirst(int key)
      • 🍁2.2.2 addLast(int key)
      • 🍁2.2.3 addIndex(int key, int index)
      • 🍁2.2.4 remove(int key)和removeAllKey(int key)
      • 🍁2.2.5 clear()
  • 🍁3. Java中LinkedList的使用
    • 🍁3.1 LinkedList的创建和使用
    • 🍁3.2 LinkedList的遍历
  • 🍁4. ArrayList和LinkedList的区别

🚀欢迎互三👉: 2的n次方_💎💎
🚀所属专栏:数据结构与算法学习💎💎

在这里插入图片描述

在这里插入图片描述

🍁1. 链表的介绍

链表是数据结构中一种非常重要的基础结构,它不同于数组,链表中的元素在物理存储上并不连续,而是通过指针(或引用)连接在一起。在Java中,链表的应用非常广泛,尤其是在需要动态添加或删除元素的场景中。

🍁2. 链表的实现

🍁2.1 单向链表

单链表中的每个元素都称为节点(Node),每个节点包含两个部分:一部分存储数据(value),另一部分存储指向列表中下一个节点的引用(next)。最后一个节点的next引用为null,表示链表的结束。

所以采用内部类的形式进行创建:

public class MySingleList {static class ListNode {public int value;public ListNode next;public ListNode(int value) {this.value = value;}}public ListNode head;
}

还可以创建一个IList接口,对其中的增删查改等方法进行规范,之后MySingleList对接口进行实现

public interface IList {void display();int size();boolean contains(int key);void addFirst(int key);void addLast(int key);void addIndex(int key,int index);void remove(int key);void removeAllKey(int key);void clear();
}

接下来就是方法的实现

🍁2.1.1 size()

返回长度:

只需要将head依次往末尾移动,并记录移动次数进行返回就可以了,当head为null时就表示已经遍历完成

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

🍁2.1.2 display()

遍历打印:

遍历的话需要找到头节点,接着依次往后移动,为了不该变头节点的指向,创建一个cur节点辅助遍历,同样的,结束的标志也是最后的指向不为空

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

🍁2.1.3 contains(int key)

判断值是否存在链表中,这里同样需要依次遍历,然后比较value的值

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

🍁2.1.4 addFirst(int key),addLast(int key),addIndex(int key, int index)

头插:

头插比较简单,直接创建一个节点,并初始化值,指向原来的head节点,接着改为新的head节点

public void addFirst(int key) {ListNode node = new ListNode(key);node.next = head;head = node;}

尾插:

尾插就需要判断head节点是否为null,接着找到尾节点进行插入

public void addLast(int key) {ListNode node = new ListNode(key);//头结点为null,直接插入if (head == null) {head = node;return;}//找到尾节点进行插入ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;}

在指定索引插入:

在指定索引插入就更加麻烦一些,需要对传入的索引进行判断,如果是0.就调用头插的方法,如果等于链表的长度,就调用尾插的方法,如果是中间的索引,就遍历链表,找到该索引进行插入

public void addIndex(int key, int index) {ListNode node = new ListNode(key);//调用头插if (index == 0) {addFirst(key);return;}//调用尾插if (index == this.size()) {addLast(key);return;}//传入索引不合法的情况if (index < 0 || index > this.size()) {throw new IndexOutOfBoundsException();}//找到目标索引进行插入ListNode cur = head;while (index - 1 != 0) {cur = cur.next;index--;}node.next = cur.next;cur.next = node;}

🍁2.1.5 remove(int key),removeAllKey(int key)

删除指定元素:

如果head为空,直接返回,如果head的value就是目标元素,就把head的下一个节点作为头结点,其他情况就根据value的值寻找目标索引,如果没找到就返回,也就是cur节点为null,找到的话把cur的引用指向cur的之后第二个节点

//根据元素找到目标索引
private ListNode findIndexofKet(int key) {ListNode cur = head;while (cur.next != null) {if (cur.next.value == key) {return cur;}cur = cur.next;}return null;}public void remove(int key) {//头结点为空if (head == null) {return;}//头结点为目标元素if (head.value == key) {head = head.next;}//其他节点为目标元素ListNode cur = findIndexofKet(key);if (cur == null) {return;}cur.next = cur.next.next;}

删除所有指定元素:

需要有两个指针,同时往后遍历,删除cur节点所指元素需要将pre节点的next指向cur的next,循环判断,最后还要判断head节点是否为指定元素

public void removeAllKey(int key) {//头结点为null,直接返回if (this.head == null) {return;}ListNode pre = head;ListNode cur = head.next;//循环删除while (cur != null) {if (cur.value == key) {pre.next = cur.next;cur = cur.next;} else {pre = cur;cur = cur.next;}}//判断头结点if (head.value == key) {head = head.next;}}

🍁2.1.6 clear()

清空链表:

清空链表只需要遍历链表所有节点,将每一个节点置为null即可,因为是从头结点开始,如果直接将head置为null,后续再找head.next就会报错,所以还需要用一个中间变量cur辅助遍历

public void clear() {ListNode cur = head;while (cur != null) {//创建变量,解决空指针异常ListNode curn = cur.next;cur = null;cur = curn.next;}head = null;}

🍁2.2 双向链表

双向链表有两个指针域,一个指向前一个节点,一个指向后一个节点
在这里插入图片描述

public class MyLinkedList implements IList {static class TListNode {public int value;TListNode pre;TListNode next;public TListNode(int value) {this.value = value;}}public TListNode head;public TListNode last;
}

双向链表的size() ,display(),contains(int key)和单向链表是一样的,下面来实现其他的方法

🍁2.2.1 addFirst(int key)

头插:

在这里插入图片描述

public void addFirst(int key) {TListNode node = new TListNode(key);if (head == null) {head = last = node;} else {node.next = head;head.pre = node;head = node;}}

🍁2.2.2 addLast(int key)

尾插:

public void addLast(int key) {TListNode node = new TListNode(key);if (head == null) {head = last = node;} else {last.next = node;node.pre = last;last = last.next;}}

🍁2.2.3 addIndex(int key, int index)

指定位置插入:

public void addIndex(int key, int index) {TListNode node = new TListNode(key);if(index < 0 || index > size()) return;if (index == 0) {addFirst(key);return;}if (index == size()) {addLast(key);}if (index > 0 && index < size()) {TListNode cur = head;//可以直接到indext的位置,因为双向链表可以找到前一个节点while (index-- != 0) {cur = cur.next;}node.next = cur;cur.pre.next = node;node.pre = cur.pre;cur.pre = node;}}

需要修改四个位置,把要插入的节点node的next 指向cur,再把cur.pre的next指向node,此时节点的next都有了指向,接着node的pre指向cur.pre节点,cur的pre再指向node,此时就完成了插入
在这里插入图片描述

🍁2.2.4 remove(int key)和removeAllKey(int key)

首先找到要删除的值的索引

private TListNode findIndexofKet(int key) {TListNode cur = head;while (cur != null) {if (cur.value == key) {return cur;}cur = cur.next;}return null;}

删除的时候还要考虑是否为头结点和尾节点

public void remove(int key) {TListNode cur = findIndexofKet(key);if(cur == null){return;}//头节点的情况if(cur == head){head = cur.next;//只有一个节点时,指向next后head为null所以当head!=空时才能把pre置为nullif (head != null) {head.pre = null;}}else{cur.pre.next = cur.next;//尾节点的情况if(cur.next == null){last = last.pre;}else{cur.next.pre = cur.pre;}}}

相比于单向链表,双向链表的删除所有指定元素就非常简单了,只需要在原来删除一个的基础上稍加修改就可以了

public void removeAllKey(int key) {TListNode cur = head;while (cur != null) {if (cur.value == key) {if (cur == head) {head = cur.next;if (head != null) {head.pre = null;}} else {cur.pre.next = cur.next;if (cur.next == null) {last = last.pre;} else {cur.next.pre = cur.pre;}}}cur = cur.next;}}

🍁2.2.5 clear()

清空链表还是依次遍历每一个节点,把每一个节点都置为null,最后把head和last也置为null

public void clear() {TListNode cur = head;while(cur.next!=null){TListNode curn = cur;curn.pre = null;curn.next = null;cur = curn;}head = last = null;}

🍁3. Java中LinkedList的使用

🍁3.1 LinkedList的创建和使用

在上一篇数据结构ArrayList的讲解中已经简单提到过👉点我看回顾,集合的一些基本框架,LinkedList也实现了List接口,所以也可以通过接口创建对象,也可以使用List接口中的方法

public class Demo {public static void main(String[] args) {LinkedList<Integer> list1 = new LinkedList<>();List<Integer> list2 = new LinkedList<>();list1.add(1);list1.add(2);System.out.println(list1);list2.add(1);list2.add(3);System.out.println(list2);}
}

在这里插入图片描述
可以直接对LinkedList的对象进行打印,也就是说LinkedList重写了toSting方法
在这里插入图片描述
这些都是LinkedList中独有的方法,这里就不列举使用了,

🍁3.2 LinkedList的遍历

LinkedList的遍历和ArrayList的遍历方式一样,在上一篇介绍了五种遍历方式,这次再简单回顾一下

public class Demo {public static void main(String[] args) {LinkedList<Integer> list1 = new LinkedList<>();list1.add(1);list1.add(2);list1.add(3);list1.add(4);//迭代器 ListIteratorListIterator<Integer> lit = list1.listIterator();while(lit.hasNext()){System.out.print(lit.next() + " ");}System.out.println();//IteratorIterator<Integer> it = list1.iterator();while(it.hasNext()){System.out.print(it.next() + " ");}System.out.println();//增强forfor(Integer l : list1){System.out.print(l + " ");}System.out.println();//普通forfor(int i = 0;i < list1.size();i++){System.out.print(list1.get(i) + " ");}System.out.println();//lambda表达式list1.forEach(e -> {System.out.print(e + " ");});System.out.println();}
}

🍁4. ArrayList和LinkedList的区别

在这里插入图片描述
ArrayList底层是一个动态数组
LinkedList底层是双向链表
ArrayList:访问元素的时间复杂度为 O(1)(直接通过索引)。
LinkedList:访问元素的时间复杂度为 O(n)(需要从头或尾开始遍历到目标位置)。
ArrayList:
在末尾添加元素的时间复杂度为 O(1)
在中间插入或删除元素时,时间复杂度为 O(n),因为需要移动其他元素以保持连续的内存块。
LinkedList:
在任意位置添加或删除元素的时间复杂度为 O(1),只需改变前后节点的指针(需要先找到目标位置,时间复杂度为 O(n))。

使用场景:
ArrayList:
适合频繁读取、随机访问元素的场景。
如需要大量顺序读写、索引访问操作。
LinkedList:
适合频繁插入、删除元素的场景,尤其是在列表中间进行操作。
如需要频繁的增删操作,但不常用到随机访问。

在这里插入图片描述

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

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

相关文章

墨西哥:海外新闻稿媒体分发-海外pr发稿干货分享-大舍传媒

大舍传媒&#xff1a;海外新闻稿媒体分发平台 墨西哥观查者 (mexicoviewer) 墨西哥观查者是墨西哥一家知名的新闻媒体平台&#xff0c;该平台专注于报道墨西哥国内外的时事新闻、政治、经济、文化等多个领域的内容。其更新速度快&#xff0c;报道对象广泛&#xff0c;深受墨西…

开始性能测试之前的准备工作!

性能测试是软件测试中不可或缺的一部分&#xff0c;它可以帮助我们评估软件系统的性能表现&#xff0c;并找出潜在的性能瓶颈。在进行性能测试之前&#xff0c;需要做好充分的准备工作&#xff0c;以确保测试的有效性和准确性。 1. 确定性能测试的目标和范围 * 明确测试目标:性…

《数据库原理》SQLServer期末复习_题型+考点

目录 题型&#xff1a; 一. 概况分析题&#xff08;5小题&#xff0c;每小题2分&#xff0c;共10分&#xff09; 二. 计算题&#xff08;3小题&#xff0c;每小题5分&#xff0c;共15分&#xff09; 三. 数据库设计&#xff08;2小题&#xff0c;每小题10分&#xff0c;共2…

在mysql中delete和truncated的相同点和区别点

相同点 删除数据&#xff1a;两者都会删除表中的数据。影响数据&#xff1a;两者都不删除表结构&#xff0c;只影响表中的数据。 区别点 操作方式&#xff1a; DELETE&#xff1a;逐行删除数据&#xff0c;可以使用 WHERE 子句来指定删除的条件。如果不加 WHERE 子句&#…

【Python机器学习】处理文本数据——用tf-idf缩放数据

为了按照我们预计的特征信息量大小来缩放特征&#xff0c;而不是舍弃那些认为不重要的特征&#xff0c;最常见的一种做法就是使用词频-逆向文档频率&#xff08;tf-idf&#xff09;。这一方法对某个特定文档中经常出现的术语给与很高的权重&#xff0c;但是堆在语料库的许多文档…

作业/数据结构/2023/7/10

1.实现单向链表队列的&#xff0c;创建&#xff0c;入队&#xff0c;出队&#xff0c;遍历&#xff0c;长度&#xff0c;销毁。 main.c #include "head.h"int main(int argc, const char *argv[]) {//创建链式队列queue_ptr QLcreate_queue();//入栈push(QL, 1000)…

imx6ull/linux应用编程学习(16)emqx ,mqtt创建连接mqtt.fx

在很多项目中都需要自己的私人服务器&#xff0c;以保证数据的隐私性&#xff0c;这里我用的是emqx。 1.进入emqx官网 EMQX&#xff1a;用于物联网、车联网和工业物联网的企业级 MQTT 平台 点击试用cloud 申请成功后可得&#xff1a;&#xff08;右边的忽略&#xff09; 进入…

告别PS,ChatGPT图片局部修改,手把手教你成为画图高手

大家好&#xff0c;我是YUAN&#xff01; 今天&#xff0c;我要向大家介绍一个能够点燃创意火花的画图设计神器——DALLE编辑器。让艺术创作&#xff0c;尤其是画图变得更加简单、直观&#xff0c;甚至可以说是革命性的。 DALLE是什么&#xff1f; DALLE编辑器的问世&#xf…

macOS系统下载navicat安装包

链接: https://pan.baidu.com/s/1SqTIXNL-B8ZMJxIBu1DfIw?pwdc1z8 提取码: c1z8 安装后效果

05STM32EXIT外部中断中断系统

STM32EXIT外部中断&中断系统 中断系统中断触发条件&#xff1a;中断处理流程和用途&#xff1a; STM32中断NVIC嵌套中断向量控制器基本结构 中断系统 中断触发条件&#xff1a; 对外部中断来说&#xff0c;可以是引脚发生了电平跳变 对定时器来说&#xff0c;可以是定时的…

《算法笔记》总结No.6——贪心

一.简单贪心 贪心法是求解一类最优化问题的方法&#xff0c;它总是考虑在当前状态下局部最优(或较优)之后&#xff0c;来使全局的结果达到最优(或较优)的策略。显然&#xff0c;如果采取较优而非最优的策略(最优策略可能不存在或是不易想到)&#xff0c;得到的全局结果也无法是…

【深入理解JVM】关于Object o = new Object()

1. 解释一下对象的创建过程 “半初始化”状态通常指的是对象在内存分配后、但在完全初始化之前的一种状态。在Java中&#xff0c;虽然JVM的规范和设计努力避免对象处于这种不稳定的状态&#xff0c;但在多线程环境下&#xff0c;由于指令重排序等并发问题&#xff0c;仍有可能…

【LeetCode刷题笔记】LeetCode.24.两两交换链表中的节点

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

新手小白的pytorch学习第一弹-------张量

1 导入pytorch包 import torch2 创建张量&#xff08;tensor&#xff09; scalar标量 scalar torch.tensor(7) scalartensor(7)scalar.ndim查看scalar的维度&#xff0c;因为scalar是标量&#xff0c;所以维度为0 0scalar.shapetorch.Size([])torch.item()7vector&#xf…

Apache功能配置:访问控制、日志分割; 部署AWStats日志分析工具

目录 保持连接 访问控制 只允许指定ip访问 拒绝指定主机其他正常访问 用户授权 日志格式 日志分割 操作步骤 使用第三方工具cronolog分割日志 AWStats日志分析 操作步骤 访问AwStats分析系统 保持连接 Apache通过设置配置文件httpd-default.conf中相关的连接保持参…

基于Java的科大讯飞大模型API调用实现

写在前面&#xff1a;因为现在自己实习的公司新拓展的一个业务是结合AI的低代码平台&#xff0c;我负责后端的开发&#xff0c;之前一直都是直接使用gpt或者文心一言等ui界面来直接使用大模型&#xff0c;从来没有自己调接口过&#xff0c;所以本文记录一下自己第一次使用大模型…

【QT】QComboBox允许输入查询,且不区分大小写

目录 0.简介 1.环境 2.详细代码 3.参考 0.简介 项目需求&#xff0c;原本有一个下拉框&#xff0c;但是条目太多&#xff0c;不好搜索&#xff0c;所以用户要求可以输入查找 修改前 &#xff1a; 修改后&#xff1a; 1.环境 windows11 vs-code qt5.12 2.详细代码 QComboB…

MD5加密和注册页面的编写

MD5加密 1.导入包 npm install --save ts-md5 2.使用方式 import { Md5 } from ts-md5; //md5加密后的密码 const md5PwdMd5.hashStr("123456").toUpperCase(); 遇见的问题及用到的技术 注册页面 register.vue代码 <template><div class"wappe…

关于Python中的字典你所不知道的七个技巧

01 引言 Python是我最喜欢的编程语言之一&#xff0c;它向来以其简单性、多功能性和可读性而闻名。 字典作为Python中最常使用的数据类型&#xff0c;大家几乎每个人都或多或少在项目中使用过字典&#xff0c;但是字典里有一些潜在的技巧可能并不是每个同学都会用到。 在本文…

【UE5.1 角色练习】16-枪械射击——瞄准

目录 效果 步骤 一、瞄准时拉近摄像机位置 二、瞄准偏移 三、向指定方向射击 四、连发 效果 步骤 一、瞄准时拉近摄像机位置 打开角色蓝图&#xff0c;在事件图表中添加如下节点&#xff0c;当进入射击状态时设置目标臂长度为300&#xff0c;从而拉近视角。 但是这样切…