【数据结构】单链表 双向链表

news/2024/9/13 20:44:17/ 标签: 数据结构, 学习, 算法, java, intellij-idea, 链表, 学习方法

目录

  • 链表
  • 链表的分类
  • 链表
    • 链表接口的实现
    • 内部类
    • 头插法
    • 尾插法
    • 任意位置插入
    • 查找是否包含关键字key是否在单链表当中
    • 删除第一次出现关键字为key的节点
    • 删除所有值为key的节点
    • 得到单链表的长度
    • 清空链表
    • 链表的优缺点
  • 双向链表
    • 双向链表接口的实现
    • 内部类
    • 头插法
    • 尾插法
    • 任意位置插入
    • 查找是否包含关键字key是否在单链表当中
    • 删除第一次出现关键字为key的节点
    • 删除所有值为key的节点
    • 得到链表的长度
    • 清空链表
  • Java中的LinkedList
    • 实现的接口
    • 构造方法
    • 常用方法
    • 双向链表的优劣
  • ArrayList和LinkedList对比
  • 链表练习

转场

链表

链表就是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
也是线性表。
数据域:存储数据元素信息的域。
指针域:存储直接后继的信息。

<a class=链表 " />

链表的分类

链表根据三个条件分类:

  1. 有头无头:有没有头结点,头结点的数据域是无用的。
  2. 是否循环:尾结点又指回头。
  3. 单向还是双向:指针域包不包含指向前面域的指针。

根据以上3个条件来分类(每一个条件选一),链表一共有8种。

链表

链表全称为:无头单向不循环链表

链表接口的实现

自己实现一个单链表(存储int数据类型),将单链表作为一个类,我们实现一些“接口”即成员方法来实现数据的增删查改。

java">// 1、无头单向非循环链表实现public class SingleLinkedList {//头插法public void addFirst(int data);//尾插法public void addLast(int data);//任意位置插入,第一个数据节点为0号下标public boolean 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 clear();}

内部类

因为我们需要使用数据域,指针域,在链表中一个一个串起来。那我们就将数据域指针域使用一个静态内部类来封装。只将第一个节点用head来表示。

注意:此处的head不是链表分类时的头,因为分类的头的数据域的数据是无效的而此处是有效的。

java">public class SingleLinkedList {static class ListNode{public int val;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;
}

头插法

实现思路:
将当前节点的下一个节点(next)指向头(head),再改头为当前节点。

java">public void addFirst(int data) {ListNode cur = new ListNode(data);cur.next = head;head = cur;}

尾插法

实现思路:

  1. 先判断链表是否为空,为空头指向尾插节点(因为尾插涉及尾结点的next,链表如果为空就会空指针异常)。
  2. 链表不为空,使用循环找到尾结点,next指向尾插节点。
java">public void addLast(int data){if(head == null){head = new ListNode(data);}ListNode cur = head;while(cur.next != null){cur = cur.next;}cur.next = new ListNode(data);}

任意位置插入

实现思路:

  1. 先判断插入位置合法吗,不合法就抛异常。
  2. 头尾位置插入调用头插尾插函数即可。
  3. 中间节点插入找到前一个位置记录下来,当前位置记录下来, 在改插入节点和前一个节点的next。
java">public boolean addIndex(int index,int data) throws IndexIllegalException{try{if(index < 0 || index > size()){throw new IndexIllegalException("位置不合法");} else if (index == 0) {addFirst(data);return true;} else if (index == size()) {addLast(data);return true;}else{ListNode cur = head;ListNode pre = head;ListNode newNode = new ListNode(data);for (int i = 0; i < index-1; i++) {cur = cur.next;pre = pre.next;}newNode.next = cur.next;pre.next = cur;return true;}}catch(IndexIllegalException e){e.printStackTrace();return false;}}

查找是否包含关键字key是否在单链表当中

使用循环遍历即可。

java">public boolean contains(int key){ListNode cur = head;for (int i = 0; i < size(); i++) {if(cur.val == key){return true;}cur = cur.next;}return false;}

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

实现思路:

  1. 链表是否为空,空直接返回。
  2. 先看头结点是不是要删的节点(因为删除会涉及被删节点的前一个节点),是直接将头指向下一个节点。
  3. 循环找到当前节点,让前一个节点next指向当前节点的next即可。
java">public void remove(int key){if(head == null){return;} else if (head .val == key) {head = head.next;return;}ListNode cur = head.next;ListNode pre = head;while(cur != null){if(cur.val == key){pre.next = cur.next;return;}cur = cur.next;pre = pre.next;}}

删除所有值为key的节点

跟删除一个节点一个逻辑只不过删除后不返回,并且头结点最后判断。

java"> public void removeAllKey(int key){if(head == null){return;}ListNode cur = head.next;ListNode pre = head;while(cur != null){if(cur.val == key){pre.next = cur.next;cur = cur.next;}else {cur = cur.next;pre = pre.next;}}if(head.val == key){head = head.next;}}

得到单链表的长度

直接循环遍历就行。

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

清空链表

直接循环将每一个节点置空,注意置空前要将头先向后走。

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

链表的优缺点

优缺点如下:

  • 优点:单向链表增加删除节点简单。
  • 缺点:只能从头到尾遍历,只能找到后继。

双向链表

在Java的集合类中使用的是无头双向非循环链表
双向<a class=链表 " />

双向链表接口的实现

自己实现一个双向链表(存储int数据类型),将双向链表作为一个类,我们实现一些“接口”即成员方法来实现数据的增删查改。

java"> // 2、无头双向链表实现public class LinkedList {//头插法public void addFirst(int data);//尾插法public void addLast(int data);//任意位置插入,第一个数据节点为0号下标public boolean 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 clear();}

内部类

因为我们需要使用数据域,指针域,在链表中一个一个串起来。那我们就将数据域指针域使用一个静态内部类来封装。只将第一个节点用head来表示,最后一个节点用last表示。

注意:此处的head不是链表分类时的头,因为分类的头的数据域的数据是无效的而此处是有效的。

java">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;

头插法

实现思路:

  1. 如果当前链表为空就直接将头尾指向当前节点。
  2. 如果不是先将插入节点的下一个指向头,头的前一个指向插入节点,头指向插入节点。
java">    public void addFirst(int data){ListNode cur = new ListNode(data);if(head == null){head = last = cur;return;}cur.next = head;head.prev = cur;head = cur;}

尾插法

实现思路:

  1. 链表是否为空,为空直接头和尾指向插入节点。
  2. 链表不为空,将尾结点的next指向插入节点,将插入节点的prev指向尾结点,将尾结点改为插入节点。
java">public void addLast(int data){ListNode cur = new ListNode(data);if(last == null){head = last = cur;return;}last.next = cur;cur.prev = last;last = cur;}

任意位置插入

实现思路:

  1. 判断位置是否合法,不合法抛异常。
  2. 插入位置为头尾,对应调用头插方法,尾插方法。
  3. 找到插入位置对应节点,将插入节点前驱prev改为对应节点前驱,对应节点前驱改为插入节点,插入节点后继next改为对应节点。
java">public boolean addIndex(int index,int data) throws IndexIllegalException{try{if(index < 0 || index > size()){throw new IndexIllegalException("插入位置不合法");} else if (index == 0) {addFirst(data);return true;} else if (index == size()) {addLast(data);return true;}else {ListNode cur = head;ListNode newNode = new ListNode(data);for (int i = 0; i < index; i++) {cur = cur.next;}newNode.prev = cur.prev;cur.prev = newNode;newNode.next = cur;return true;}}catch (IndexIllegalException e){e.printStackTrace();return false;}}

查找是否包含关键字key是否在单链表当中

直接循环遍历就行。

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

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

实现思路:

  1. 先判断头节点是不是要删的节点,是将下一个节点前驱prev置为空,头指向下一个节点,返回。
  2. 循环遍历除头尾节点外的节点,找到被删节点,将该节点前一个节点的后继next改为该节点的下一个节点,该节点后一个节点的前驱prev改为该节点的前一个,返回。
  3. 如果都不是,判断尾节点,将尾结点的前一个节点的后继next置为空,尾结点改为前一个节点,返回。
java">public void remove(int key){ListNode cur = head;if(head.val == key){head.next.prev = null;head = head.next;return;} else {while(cur.next != null){if(cur.val == key){cur.prev.next = cur.next;cur.next.prev = cur.prev;return;}cur = cur.next;}if (last.val == key) {last.prev.next = null;last = last.prev;return;}}}

删除所有值为key的节点

  1. 先循环遍历除头尾节点外的节点,找到被删节点,将该节点前一个节点的后继next改为该节点的下一个节点,该节点后一个节点的前驱prev改为该节点的前一个。
  2. 判断头节点是不是要删的节点,是将下一个节点前驱prev置为空,头指向下一个节点。
  3. 判断尾节点,将尾结点的前一个节点的后继next置为空,尾结点改为前一个节点。
java">public void removeAllKey(int key){ListNode cur = head.next;while(cur.next != null){if(cur.val == key){cur.prev.next = cur.next;cur.next.prev = cur.prev;}cur = cur.next;}if(head.val == key){head.next.prev = null;head = head.next;}if (last.val == key) {last.prev.next = null;last = last.prev;}}

得到链表的长度

直接循环遍历即可。

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

清空链表

实现思路:

  1. 先循环遍历将每一个节点前驱prev后继next置为空。
  2. 最后将头head和为last置为空。
java">public void clear(){ListNode cur = head;while(cur != null){ListNode curNext = cur.next;cur.prev = null;cur.next = null;cur = curNext;}head = null;last = null;}
}

Java中的LinkedList

在Java中用集合类LinkedList来表示双向链表

实现的接口

接口

接口说明:

  • 没有实现RandomAccess接口,不能随机访问。
  • 实现了Cloneable接口,可克隆。
  • Serializable接口表示支持序列化。

构造方法

Java中提供了两个构造方法。

方法方法用途介绍
LinkedList()无参构造
public LinkedList(Collection<? extends E> c)使用其他集合容器中元素构造List

常用方法

提供的常用方法与上面实现的差不多。
在这里插入
图片描述

双向链表的优劣

优缺点如下:

  • 优点:可以找到前驱和后继,可进可退。
  • 确点:删除节点复杂,需要多分配一个指针域。

ArrayList和LinkedList对比

对比如下图:
对比

链表练习

删除链表中等于给定值 val 的所有节点
反转一个单链表
链表的中间节点
合并两个有序链表
链表分割
链表的回文结构
相交链表
环形链表


http://www.ppmy.cn/news/1475526.html

相关文章

微信小程序 - 本地存储 增加有效期

小程序的本地存储API提供了wx.setStorageSync和wx.setStorage来存储数据&#xff0c;注意的是&#xff0c;小程序的本地存储并没有明确的有效期设置&#xff0c;存储的数据在不超过限制的情况下&#xff0c;会一直保留。 一、小程序本地存储API 小程序的本地存储API提供了设置…

32路串口服务器 应用领域

32路串口服务器在多个领域有着广泛的应用&#xff0c;以下是详细的应用实例&#xff1a; 一、工业自动化 在工业自动化领域&#xff0c;32路串口服务器发挥着举足轻重的作用。传统的工业设备往往采用串口通信方式&#xff0c;而串口服务器能够将这些设备接入网络&#xff0c;…

800 元打造家庭版 SOC 安全运营中心

今天,我们开始一系列新的文章,将从独特而全面的角度探索网络安全世界,结合安全双方:红队和蓝队。 这种方法通常称为“紫队”,集成了进攻和防御技术,以提供对威胁和安全解决方案的全面了解。 在本系列的第一篇文章中,我们将指导您完成以 100 欧元约800元左右的预算创建…

金南瓜科技的SECS/GEM解决方案

1. 高稳定性和可靠性&#xff1a;金南瓜SECS/GEM解决方案已在多家知名半导体工厂中稳定运行&#xff0c;实现了7*24小时无重启无故障超过3年时间 。 2. 符合国际标准&#xff1a;金南瓜的解决方案符合SEMI E4、E5、E30、E37通信协议&#xff0c;确保与国际标准的兼容性&#x…

《RWKV》论文笔记

原文出处 [2305.13048] RWKV: Reinventing RNNs for the Transformer Era (arxiv.org) 原文笔记 What RWKV(RawKuv):Reinventing RNNs for the Transformer Era 本文贡献如下&#xff1a; 提出了 RWKV 网络架构&#xff0c;结合了RNNS 和Transformer 的优点&#xff0c;同…

星环科技推出语料开发工具TCS,重塑语料管理与应用新纪元

5月30-31日&#xff0c;2024向星力未来数据技术峰会期间&#xff0c;星环科技推出一款创新的语料开发工具——星环语料开发工具TCS&#xff08;Transwarp Corpus Studio&#xff09;&#xff0c;旨在通过全面的语料生命周期管理&#xff0c;极大提升语料开发效率&#xff0c;助…

prompt第三讲-PromptTemplate

文章目录 前提回顾PromptTemplateprompt 模板定义以f-string渲染格式以mustache渲染格式以jinja2渲染格式直接实例化PromptTemplatePromptTemplate核心变量 prompt value生成invokeformat_prompt(不建议使用)format(不建议使用) batchstreamainvoke PromptTemplate核心方法part…

Redis实战—秒杀优化(Redis消息队列)

目录 回顾 基于阻塞队列实现程序异步优化 优化代码 总结 Redis消息队列 初始消息队列 基于List结构模拟消息队列 基于PubSub的消息队列 基于Stream的消息队列 基于Stream的消息队列-消费者组 总结 代码实现 回顾 我们回顾一下前文下单的流程&#xff0c;当用户发起…

python:使用openpyxl模块处理excel

前言 最近在实践excel的处理&#xff0c;在此途中&#xff0c;我彻底抛弃了xlwt xlrd的组合&#xff0c;投入了openpyxl这一模块的怀抱。 并成功实现了excel单元格数据的快速访问、修改、样式保持&#xff0c;以及添加填充色等功能。 至于为什么写这个博客&#xff0c;主要是因…

Interpretability 与 Explainability机器学习

在机器学习的范畴中&#xff0c;“Interpretability”&#xff08;可解释性&#xff09;和“Explainability”&#xff08;可解释性&#xff09;尽管在含义上有重叠部分&#xff0c;但仍存在一些微妙的差异和重点的不同。 “Interpretability”主要强调模型自身的结构和运作方式…

科普文:微服务技术栈梳理

概叙 如上两图所示&#xff0c;微服务架构下&#xff0c;需要的组件很多&#xff0c;上面中也并未列全。下面将梳理一下国内微服务架构下&#xff0c;用到的技术栈&#xff0c;仅供参考。 科普文&#xff1a;12种常见的软件架构-CSDN博客 没有最好的架构&#xff0c;只有最适…

【Android面试八股文】组件化在项目中有什么意义?

一、没有组件化会出现什么问题? 早期的单一分层模式 问题一:无论分包怎么做,随着项目增大,项目失去层次感,后面接手的人扑街问题二:包名约束太弱,稍有不注意,就会不同业务包直接互相调用,代码高耦合问题三:多人开发在版本管理中,容易出现代码覆盖冲突等问题二、组件…

[linux] git push时需要输入user 和keyword

git clone的要是ssh链接&#xff01;&#xff01;&#xff01;&#xff01; 1、用户名和邮箱 git config --global user.name "name" git config --global user.email "email" 2、生成ssh key (ED25519) ssh-keygen -t ed25519 -C "<自定义内容&g…

数据结构:顺序表+链表

数据结构&#xff1a;顺序表链表 一。顺序表&#xff1a; 首先在了解顺序表和链表之前&#xff0c;先了解一下线性表&#xff0c;**线性表&#xff08;linear list&#xff09;**是n个具有相同特征元素的有限序列 &#xff0c;在逻辑上是线性结构&#xff0c;也就是一条连续的…

[k8s源码]2.CURD deployment

加载kubernetes配置 使用 clientcmd方法&#xff0c;是通过"k8s.io/client-go/tools/clientcmd"包加载的。这个函数返回的是config和error两个值。可以看到返回的config是一个指针变量。 func clientcmd.BuildConfigFromFlags(masterUrl string, kubeconfigPath str…

08 从项目概念到技术概念

我们从技术转型到管理的路上&#xff0c;如果没有贵人引路&#xff0c;基本上都是要先从项目管理这关过。这其实是一件好事&#xff1b;项目管理里面很多琐碎的知识点&#xff0c;非常有用。我今天想举个例子是项目管理过程中经常提到的 “输入-输出”。 ## 1. 好奇的 DDD DD…

vue父组件样式穿透修改子组件样式

在 Vue 中&#xff0c;使用父组件样式穿透到子组件通常不推荐&#xff0c;因为它破坏了样式的作用域隔离&#xff0c;但如果你确实需要这样做&#xff0c;可以使用深度选择器。Vue 2 使用 ::v-deep&#xff0c;而 Vue 3 使用 /deep/ 或 ::v-deep 都可以。 以下是使用深度选择器…

无法访问。你可能没有权限使用网络资源。请与这台服务器的管理员联系以查明你是否有访问权限。【解决办法】

问题描述 新建好一台windows虚拟机&#xff0c;两台设备网络是互通的&#xff0c;但是物理机在访问虚拟机的网络共享文件资源时&#xff0c;出现图下所示的报错&#xff1a;XXX无法访问。你可能没有权限使用网络资源。请与这台服务器的管理员联系以查明你是否有访问权限。用户…

如何解决数据分析问题:IPython与Pandas结合

如何解决数据分析问题&#xff1a;IPython与Pandas结合 数据分析是现代科学研究、商业决策和技术开发中的一个重要环节。IPython和Pandas是两个强大的工具&#xff0c;它们可以大大简化和加速数据分析的过程。本文将为初学者详细介绍如何结合使用IPython和Pandas来解决数据分析…

Spring Boot中处理同名Bean冲突的解决办法

核心问题&#xff1a;在Spring Boot项目中&#xff0c;同名Bean的冲突可能导致ConflictingBeanDefinitionException异常。 解决策略&#xff1a; 更换类名&#xff1a; 当两个类未手动设置Bean名称时&#xff0c;修改其中一个类名以避免冲突。 手动设置Bean的名称&#xff1a…