链表知识回顾
类型:单链表,双链表、循环链表

存储:在内存中不是连续存储
删除操作:即让c的指针指向e即可,无需释放d,因为java中又内存回收机制
添加节点:


链表的构造函数
public class ListNode {// 结点的值int val;// 下一个结点ListNode next;// 节点的构造函数(无参)public ListNode() {}// 节点的构造函数(有一个参数)public ListNode(int val) {this.val = val;}// 节点的构造函数(有两个参数)public ListNode(int val, ListNode next) {this.val = val;this.next = next;}
}
可以直接用java自带的LinkedList类实现链表的初始化
import java.util.LinkedList;public class Main {public static void main(String[] args) {// 创建一个空的链表LinkedList<Integer> list = new LinkedList<>();// 向链表中添加元素list.add(1);list.add(2);list.add(3);// 打印链表内容System.out.println(list);}
}
链表的声明:
Java标准库提供了LinkedList类,位于java.util包中。它的特点如下:
- 实现细节:
LinkedList底层通常实现为双向链表,这意味着每个节点除了指向下一个节点,还保存对前一个节点的引用。 - 接口实现:除了实现
List接口外,LinkedList还实现了Deque接口,使其既可以当作列表使用,也可以当作队列(或双端队列)使用。 - 增删操作:
add(),remove(),offer(),poll()等方法在链表头尾插入或删除元素时性能较高。 - 遍历操作:由于链表没有下标索引,随机访问通常较慢。如果频繁使用随机访问,可以考虑使用
ArrayList,ArrayList底层是基于动态数组实现的
LinkedList<Integer> list = new LinkedList<Integer>();
List<Integer> list1 =new LinkedList<Integer>();
List<Integer> list2 =new ArrayList<>();
LinkedList list3 = new LinkedList();
上述是常见的链表的声明方式
第一种,变量的声明类型和实际实现类型都是LinkedList,可直接调用 LinkedList 类中特有的方法,并且声明了泛型Integer,确保只能存储 Integer 类型的数据,编译期间就能进行类型检查,避免了类型转换异常。
第二种,声明类型是接口 List类型,实际实现的类型确实LinkedList,但只能调用 List 接口中定义的方法。如果需要使用 LinkedList 特有的方法(如队列或双端队列相关的方法),则需要显式地进行类型转换。同样使用了泛型 <Integer>,确保类型安全。
第三种声明类型是接口List,实现的是ArrayList类型,ArrayList 支持快速随机访问,时间复杂度为 O(1)。在数组中间插入或删除元素时需要移动元素,时间复杂度为 O(n);而 LinkedList 在任意位置添加或删除(假如已经有相应节点引用)通常更高效。
第四种实现的是原始类型(因为没有使用泛型),编译器不会对集合中的数据进行类型检查,
手搓链表
public class Linked {static class Node{int data;Node next;public Node(){};public Node(int data){this.data = data;this.next =null;}}public static class SingleLinkedList{private Node head;public void addFirst(int data){Node newNode = new Node(data);newNode.next = head;head = newNode;}public void addLast(int data){Node newNode = new Node(data);if(head ==null){head = newNode;return;}Node curr = head;while(curr.next != null){curr = curr.next;}curr.next = newNode;}public boolean remove(int data){if(head == null){//若链表为空,则删除失败return false;}if(head.data == data){//还要先判断头节点是否时要删除的head = head.next;return true;}Node curr = head;while(curr.next != null && curr.next.data != data){curr = curr.next;}if (curr.next != null) {curr.next = curr.next.next;return true;}return false;}public void printList() {Node curr = head;while (curr != null) {System.out.print(curr.data + " ");curr = curr.next;}System.out.println("null");}}public static void main(String[] args) {SingleLinkedList list = new SingleLinkedList();list.addFirst(4);list.addFirst(3);list.addFirst(2);list.addFirst(1);list.addLast(5);list.addLast(6);list.printList();//1 2 3 4 5 6 nulllist.remove(6);list.printList();//1 2 3 4 5 null}
}
反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL,即右移
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseList(ListNode head) {// 如果链表为空,则直接返回nullif(head == null){return null;}// prev指针初始化为null,最后会成为反转后链表的头节点ListNode prev = null;// cur指针指向当前要处理的节点,开始时指向链表头headListNode cur = head;// temp用于保存当前节点cur的下一个节点,防止在改变指针关系后丢失引用ListNode temp = null;// 当当前节点不为空时,循环执行反转操作while (cur != null) {// 保存cur的下一个节点,防止链表断裂temp = cur.next; // 此时temp指向cur的下一节点// 将当前节点的next指针指向前一个节点,实现局部反转cur.next = prev; // 当前节点的next由原来的下一个节点变为前一个节点// 将prev移动到当前节点位置,为下一次反转操作做准备prev = cur;// 将cur后移到下一个节点,也就是之前保存的tempcur = temp;}// 循环结束后,prev指向反转后的链表头节点,返回它作为新的链表起点return prev;}
}
链表内两两交换
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
解1:
- 创建一个虚拟头节点dummy指向head,定义current指针真相虚拟头节点
- 进入循环体内,确定current每次后面都能有两个节点进行交换操作
- 定义first和second分别指向第一个节点和第二个节点,
- 然后让第二个节点指向第一个节点,第一个节点指向下一个要交换的第一个节点,最后让current指向交换后的第一个节点
- 2,3,4循环操作,直至不够节点互换退出循环,返回虚拟头节点之后的head
public class Solution {public ListNode swapPairs(ListNode head) {// 创建哑结点,它的 next 指向原链表的头ListNode dummy = new ListNode(0);dummy.next = head;ListNode current = dummy;// 循环条件:当前结点后面至少有两个节点while (current.next != null && current.next.next != null) {// 定义要交换的两个节点ListNode first = current.next;ListNode second = current.next.next;// 交换节点:// 1. 先将 first 指向 second 的下一个节点first.next = second.next;// 2. second 指向 firstsecond.next = first;// 3. current 指向 second,完成与前面的连接current.next = second;// 移动 current,跳过刚才交换的两个节点current = first;}// 返回哑结点的 next,即新的头节点return dummy.next;}
}
解2:将链表转换为队列处理,在重建链表
import java.util.ArrayList;
import java.util.List;public class Solution {public ListNode swapPairs(ListNode head) {if (head == null || head.next == null) {return head;}// 1. 将链表节点存入数组List<ListNode> nodes = new ArrayList<>();ListNode current = head;while (current != null) {nodes.add(current);current = current.next;}// 2. 在数组中交换相邻节点for (int i = 0; i < nodes.size() - 1; i += 2) {// 交换nodes[i]与nodes[i+1]ListNode temp = nodes.get(i);nodes.set(i, nodes.get(i+1));nodes.set(i+1, temp);}// 3. 重建链表(根据交换后的数组重设每个节点的next指针)for (int i = 0; i < nodes.size() - 1; i++) {nodes.get(i).next = nodes.get(i + 1);}// 最后一个节点的next设为nullnodes.get(nodes.size() - 1).next = null;// 4. 返回新的链表头(数组中第一个元素)return nodes.get(0);}
}
删除链表中倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解1:首先找到长度,然后循环遍历找到那个结点的位置在删除,但过程中没有考虑到如果删除到头节点的情况
public ListNode removeNthFromEnd(ListNode head, int n) {// 1) 先算长度int len = 0;for (ListNode cur = head; cur != null; cur = cur.next) {len++;}// 2) n == len,删头if (n == len) {return head.next;}// 3) 找到第 (len - n) 个节点,它的 next 就是要删的ListNode cur = head;for (int i = 1; i < len - n; i++) {cur = cur.next;}// 4) 删除cur.next = cur.next.next;return head;
}
解2:利用虚拟头节点,和快慢指针,快指针先走n步,然后快慢指针一块走直至快指针走到头,这是慢指针指向待删除节点的前一个节点。创建虚拟头节点时,记得让虚拟头节点指向head
为什么尽量推荐使用虚拟头节点,因为可以避免头节点被删除或者更换情况。
设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummy = new ListNode(-1);dummy.next = head;ListNode first =dummy;ListNode slow = dummy;for(int i=0;i<n;i++) {first = first.next;}while(first.next != null){first = first.next;slow = slow.next;}slow.next = slow.next.next;return dummy.next;
}
}
链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思想:这里并不是简单的找到数值相等的节点,而是找到指针相等的节点,所以大概步骤就是;找到AB链表的长度,先让A指针走到和B链表长度一样的地方一起走,若有相同的节点,则返回值

public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {int lenA=0,lenB=0;for (ListNode cur = headA; cur != null; cur = cur.next) {lenA++;}for (ListNode cur = headB; cur != null; cur = cur.next) {lenB++;}ListNode A = (lenA >= lenB) ? headA : headB;ListNode B = (lenA >= lenB) ? headB : headA;int gap = Math.abs(lenA - lenB);while(gap > 0){A = A.next;gap --;}while(A != null && B != null){if(A == B) return A;A = A.next;B = B.next;}return null;}
}
环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
思想:利用快慢指针,让快指针一次走两个,慢指针一次走一个,若有环则一定会相交。
public class Solution {public boolean hasCycle(ListNode head) {if (head == null || head.next == null) {return false;}ListNode slow = head;ListNode fast = head;// 注意这里用 &&,两者都非 null 时才能继续下走while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if (fast == slow) {return true;}}return false;}
}
环形链表2
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
思想:首先,利用快慢指针判断是否有环,并且在快慢指针相等的地方做标记,
最重要的是如何找到环的入口,在定义指向头结点的指针和指向快慢相等结点的指针,两个指针同时向前走,当相遇时即是环的入口。
public class Solution {public ListNode detectCycle(ListNode head) {ListNode slow = head;ListNode fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;if (slow == fast) {// 有环ListNode index1 = fast;ListNode index2 = head;// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口while (index1 != index2) {index1 = index1.next;index2 = index2.next;}return index1;}}return null;}
}
相关文章:
链表知识回顾
类型:单链表,双链表、循环链表 存储:在内存中不是连续存储 删除操作:即让c的指针指向e即可,无需释放d,因为java中又内存回收机制 添加节点: 链表的构造函数 public class ListNode {// 结点…...
FPGA学习(五)——DDS信号发生器设计
FPGA学习(五)——DDS信号发生器设计 目录 FPGA学习(五)——DDS信号发生器设计一、FPGA开发中常用IP核——ROM/RAM/FIFO1、ROM简介2、ROM文件的设置(1)直接编辑法(2)用C语言等软件生成初始化文件 3、ROM IP核配置调用 二、DDS信号发…...
【数据结构入门训练DAY-18】信息学奥赛一本通T1331-后缀表达式的值
文章目录 前言一、题目二、解题思路总结 前言 本次训练内容: 栈的复习。栈模拟四则运算计算问题的练习。训练解题思维。 一、题目 从键盘读入一个后缀表达式(字符串),只含有0-9组成的运算数及加()、减…...
OpenCv高阶(六)——图像的透视变换
目录 一、透视变换的定义与作用 二、透视变换的过程 三、OpenCV 中的透视变换函数 1. cv2.getPerspectiveTransform(src, dst) 2. cv2.warpPerspective(src, H, dsize, dstNone, flagscv2.INTER_LINEAR, borderModecv2.BORDER_CONSTANT, borderValue0) 四、文档扫描校正&a…...
性能比拼: Go vs Bun
本内容是对知名性能评测博主 Anton Putra Go (Golang) vs. Bun: Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 我对 Bun 在之前的基准测试中的出色表现感到惊讶,因此我决定将它与 Go …...
定制化 Docsify 文档框架实战分享
🌟 定制化 Docsify 文档框架实战分享 在构建前端文档平台时,我们希望拥有更友好的用户界面、便捷的搜索、清晰的目录导航以及实用的代码复制功能。借助 Docsify,我实现了以下几个方面的定制优化,分享给大家 🙌。 &…...
Qt中读写结构体字节数据
在Qt中读写结构体字节数据通常涉及将结构体转换为字节数组(QByteArray)或直接从内存中读写。以下是几种常见方法: 方法1:使用QDataStream读写结构体 cpp #include <QFile> #include <QDataStream>// 定义结构体 #pragma pack(push, 1) //…...
鸿蒙ArkUI之布局实战,线性布局(Column,Row)、弹性布局(Flex)、层叠布局(Stack),详细用法
本文聚焦于ArkUI的布局实战,三种十分重要的布局,线性布局、弹性布局、层叠布局,在实际开发过程中这几种布局方法都十分常见,下面直接上手 线性布局 垂直布局(Column) 官方文档: Column-行列…...
测试基础笔记第七天
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、cat命令二、ls -al命令三、>重定向符号四、>>追加重定向符号五、less/more命令六、grep命令七、|管道符八、clear命令九、head命令十、tail命令十一、…...
[Windows] Adobe Camera Raw 17.2 win/Mac版本
[Windows] Adobe Camera Raw 链接:https://pan.xunlei.com/s/VOOIAXoyaZcKAkf_NdP-qw_6A1?pwdpd5k# Adobe Camera Raw,支持Photoshop,lightroom等Adobe系列软件,对相片无损格式进行编辑调色。 支持PS LR 2022 2023 2024 2025版…...
开源模型应用落地-Podcastfy-从文本到声音的智能跃迁-Gradio(一)
一、前言 在当今信息呈现方式越来越多样化的背景下,如何将文字、图片甚至视频高效转化为可听的音频体验,已经成为内容创作者、教育者和研究者们共同关注的重要话题。Podcastfy是一款基于Python的开源工具,它专注于将多种形式的内容智能转换成…...
深入剖析 Java Web 项目序列化:方案选型与最佳实践
在 Java Web 开发中,“序列化”是一个你无法绕过的概念。无论是缓存数据、共享 Session,还是进行远程过程调用(RPC)或消息传递,序列化都扮演着底层数据搬运工的角色。它负责将内存中的 Java 对象转换成可传输或可存储的…...
Python 深度学习实战 第11章 自然语言处理(NLP)实例
Python 深度学习实战 第11章 自然语言处理(NLP)实例 内容概要 第11章深入探讨了自然语言处理(NLP)的深度学习应用,涵盖了从文本预处理到序列到序列学习的多种技术。本章通过IMDB电影评论情感分类和英西翻译任务,详细介绍了如何使…...
零基础上手Python数据分析 (19):Matplotlib 高级图表定制 - 精雕细琢,让你的图表脱颖而出!
写在前面 —— 超越默认样式,掌握 Matplotlib 精细控制,打造专业级可视化图表 上一篇博客,我们学习了 Matplotlib 的基础绘图功能,掌握了如何绘制常见的折线图、柱状图、散点图和饼图,并进行了基本的图表元素定制,例如添加标题、标签、图例等。 这些基础技能已经能让我…...
将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据
文章目录 项目简介本项目分两大模块 GiteeMCP 简介环境要求项目代码核心实现代码MCP 服务端MCP 客户端 DeepSeek APIDockersse 连接ws 连接(推荐)http 连接 vue2-chat-windowCherry Studio配置模型配置 MCP调用 MCP 项目简介 在本项目中,我们…...
《前端面试题之 Vue 篇(第三集)》
目录 1、 nvm的常用命令①.Node.js 版本与 npm 版本的对应关系②Vue2 与 Vue3 项目的 Node.js 版本分界线③版本管理实践建议 2、Vue2 项目搭建(基于 vue-cli Webpack)① 环境准备② 安装 Vue CLI(脚手架)③.创建项目(…...
PHP实现图片自动添加水印效果
<?php // 设置原始图片路径和水印图片路径 $original_image original.jpg; $watermark_image watermark.png;// 创建图片资源 $original imagecreatefromjpeg($original_image); $watermark imagecreatefrompng($watermark_image);// 获取图片尺寸 $original_width im…...
嵌入式C语言位操作的几种常见用法
作为一名老单片机工程师,我承认,当年刚入行的时候,最怕的就是看那些密密麻麻的寄存器定义,以及那些让人眼花缭乱的位操作。 尤其是遇到那种“明明改了寄存器,硬件就是不听话”的情况,简直想把示波器砸了&am…...
基于Djiango实现中药材数据分析与可视化系统
中药材数据分析与可视化系统 项目截图 登录 注册 首页 药材Top20 药材价格 产地占比 历史价格 新闻资讯 后台管理 一、项目概述 中药材数据分析与可视化系统是一个基于Django框架开发的专业Web应用,致力于对各类中药材数据进行全面、系统的采集、分析和可视化展示…...
stm32(gpio的四种输出)
其实GPIO这个片上外设的功能: 用于控制IO引脚。 CPU就如同大脑,而这些片上外设就如同四肢一样的关系 如图 —————————————————————————————— OK类比了以上 其实GPIO是有 八种工作模式的 这八种工作模式 因为GPIO是面向IO…...
系统架构设计师:计算机组成与体系结构(如CPU、存储系统、I/O系统)案例分析与简答题、详细解析与评分要点
计算机组成与体系结构 10道案例分析与简答题 案例分析题(5道) 1. Cache映射与主存编址计算 场景:某计算机系统采用32位地址总线,主存容量为4GB,Cache容量为512KB,块大小为64B,使用4路组相联映射…...
Zookeeper 可观测性最佳实践
Zookeeper 介绍 ZooKeeper 是一个开源的分布式协调服务,用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题,如数据同步、配置管理、命名服务和集群管理等。本文介绍通过 DataKit 采集 Zookeeper 指标&#…...
位运算---总结
位运算 基础 1. & 运算符 : 有 0 就是 0 2. | 运算符 : 有 1 就是 1 3. ^ 运算符 : 相同为0 相异为1 and 无进位相加位运算的优选级 不用在意优先级,能加括号就加括号给一个数 n ,确定它的二进制位中第 x 位是 0 还是 1? 规定: 题中所说的第x位指:int 在32位机器下4个…...
2. 什么是最普通的自动化“裸奔状态”?
什么是最普通的自动化"裸奔状态"?从大厂案例看测试代码的生存困境 一个典型的"裸奔代码"示例 # 打开目标网站 driver.get(http://test-site.com/login-page)# 登录操作 driver.find_element_by_id(user).send_keys(tester) driver.find_eleme…...
头歌java课程实验(函数式接口及lambda表达式)
第1关:利用lambda表达式对Book数组按多个字段进行排序 任务描述 本关任务:利用Comparator接口完成对Book数组同时按多个字段进行排序。 编程要求 1、本任务共有三个文件,可查看各文件的内容 2、无需修改SortBy.java枚举文件及Book.java类文…...
微信小程序三种裁剪动画有效果图
效果图 .wxml <image class"img inset {{status?action1:}}" src"{{src}}" /> <image class"img circle {{status?action2:}}" src"{{src}}" /> <image class"img polygon {{status?action3:}}" src&quo…...
C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用
结构体 目录: 1、结构体类型声明 2、结构体变量的创建和初始化 3、结构体成员访问操作符 4、结构体内存对齐*****(重要指数五颗星) 5、结构体传参 6、结构体实现位段 一、结构体类型声明 其实在指针中我们已经讲解了一些结构体内容了&…...
git清理--解决.git文件过大问题
背景:为什么.git比我仓库中的文件大很多 为什么我的git中只有一个1KB的README,但是.git却又1G多?当我想把这个git库push到gitee时,还会报错: 根据报错信息,可看出失败的原因是:有文件的大小超过…...
Jetson Orin NX 部署YOLOv12笔记
步骤一.创建虚拟环境 conda create -n yolov12 python3.8.20 注意:YOLOv12/YOLOv11/YOLOv10/YOLOv9/YOLOv8/YOLOv7a/YOLOv5 环境通用 步骤二.激活虚拟环境 conda activate yolov12 #激活环境 步骤三.查询Jetpack出厂版本 Jetson系列平台各型号支持的最高Jetp…...
微服务2--服务治理与服务调用
前言 :本文主要阐述微服务架构中的服务治理,以及Nacos环境搭建、服务注册、服务调用,负载均衡以及Feign实现服务调用。 服务治理 服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。 服务注册:在…...
