当前位置: 首页 > news >正文

【LeetCode刷题日记:24】两两交换链表

🔥个人主页:北极的代码(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb
命运的结局尽可永在,不屈的挑战却不可须臾或缺!

前言:今天继续学习链表的相关题目,主要还是考察链表的基本操作,是面试的基础题常考,需要掌握。

题目背景:LeetCode24

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

输入:head = [1,2,3,4]输出:[2,1,4,3]

示例 2:

输入:head = []输出:[]

示例 3:

输入:head = [1]输出:[1]

提示:

  • 链表中节点的数目在范围[0, 100]
  • 0 <= Node.val <= 100

题目答案:

第一种解法:

class Solution { public ListNode swapPairs(ListNode head) { ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点 dumyhead.next = head; // 将虚拟头结点指向head,这样方便后面做删除操作 ListNode cur = dumyhead; ListNode temp; // 临时节点,保存两个节点后面的节点 ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点 ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点 while (cur.next != null && cur.next.next != null) { temp = cur.next.next.next; firstnode = cur.next; secondnode = cur.next.next; cur.next = secondnode; // 步骤一 secondnode.next = firstnode; // 步骤二 firstnode.next = temp; // 步骤三 cur = firstnode; // cur移动,准备下一轮交换 } return dumyhead.next; } }
题目解析:

我们这里仍旧使用虚拟头节点的方式,在做这种题目的时候,我们最好要把流程图画出来,不然容易搞混。

如图所示:我们把虚拟头节点设为cur,然后分别用临时节点保存需要移动的节点,我们这里保存的是第一个和第二个节点,关于temp记录的cur.next.next.next,实际上是为了步骤三交换位置。记录完之后,我们就可以开始交换了

首先cur.next=secondnode,secondnode.next=firstnode;

之后,交换后的firstnode节点的next,实际上就是我们下次开始执行交换的节点的下个节点,也就是我们保存的临时节点temp。

因为是两两交换,自然就是虚拟头节点的next.next.next,这里没什么深究的,而此时的firstnode也就相当于一开始虚拟头节点的作用,用于联系交换后面两个节点。

然后就是关于循环条件

如果链表有偶数个,那么循环的终止条件就是cur.next==null的时候,如图所示,执行完两次交换之后,此时的cur等于被交换之后val==3的那个位置

如图,因为是偶数节点,那么他后面的节点自然就是null

如果是奇数节点我们画个图理解一下,那么也就是cur节点的next.next节点为null,因为cur.next的节点没有节点与之交换。

注意:这两个条件的位置一定不能交换,否则会报空指针异常

cur.next.next != null // 先执行这个! // cur.next = 节点1 // cur.next.next = 节点1.next = null // 访问 null.next → 💥 空指针异常!

注意:

其实我们在设置临时节点的时候,只需要保存cur.next和cur.next.next.next,的值,一般不需要设置第二个节点的临时节点去保存值,因为我们拿虚拟节点交换的时候

步骤一:cur.next=cur.next.next(也就是第二个节点变成了第一个节点,交换了位置)

步骤二:firstnode=cur.next.next;(这里如果不用firstnode去记录第一个节点,那么它的值已经在步骤一的时候就被覆盖了

步骤三:照常

第二种解法:

// 递归版本 class Solution { public ListNode swapPairs(ListNode head) { // base case 退出提交 if(head == null || head.next == null) return head; // 获取当前节点的下一个节点 ListNode next = head.next; // 进行递归 ListNode newNode = swapPairs(next.next); // 这里进行交换 next.next = head; head.next = newNode; return next; } }
题目解析:

其实递归的本质就是代替循环的操作

链表初始状态:
1 → 2 → 3 → 4 → null


第一层调用swapPairs(1)

  • head = 1head.next = 2,不为 null,不满足 base case

  • next = head.next = 2

  • 进入递归newNode = swapPairs(3)(因为next.next = 3


第二层调用swapPairs(3)

  • head = 3head.next = 4,不为 null

  • next = head.next = 4

  • 进入递归newNode = swapPairs(5)(因为next.next = null


第三层调用swapPairs(5)

  • head = null→ 满足 base case,返回null

  • 返回到第二层:newNode = null


回到第二层swapPairs(3)

  • next = 4newNode = null

  • 交换操作:

    1. next.next = head4.next = 3

    2. head.next = newNode3.next = null

  • 此时链表局部:4 → 3 → null

  • 返回next→ 返回节点 4

状态:第二层返回到第一层的newNode是节点 4,且它带着4 → 3 → null这一段。


回到第一层swapPairs(1)

  • next = 2newNode = 4 → 3 → null

  • 交换操作:

    1. next.next = head2.next = 1

    2. head.next = newNode1.next = 4

注意此时1.next = 4,所以链表变成:
2 → 1 → 4 → 3 → null


最终返回

next = 2,作为新的头节点返回。

完整结果:2 → 1 → 4 → 3 → null


关键点总结:

  • 递归从链表尾部向前建立两两交换后的链表

  • 每次递归返回的是交换完这一对后,这一段的头节点(即原next节点)

  • newNode接收的是已经交换好的后续部分,然后挂在head.next

补充:
变量作用是否移动
dummyhead永远指向虚拟头结点,用于最后返回❌ 不移动
cur当前操作位置,需要不断移动✅ 移动

为什么用cur.next而不是dummyhead.next

因为cur在移动,dummyhead不动。当cur移动到后面时,cur.nextdummyhead.next就完全不同了!

  • cur.next:当前要交换的节点对的前一个节点

  • dummyhead.next:整个链表的头节点(交换后会变化)

结语:

如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!

http://www.jsqmd.com/news/591516/

相关文章:

  • WiFi感知技术全解析:从原理到实践的创新应用指南
  • 大麦自动抢票终极指南:5分钟配置,轻松告别手速焦虑
  • 【飞机】飞机的固有频率和模态形状仿真【含Matlab源码 15294期】
  • OpenMMD:开源3D动作转换工具的技术解析与实践指南
  • 实现表贴式PMSM超前角弱磁控制策略,开启弱磁后速度提升至4000rpm,不开启则仅能达到20...
  • 跨平台资源下载神器res-downloader:5分钟掌握全网视频音频下载技巧
  • 3种颠覆性方法:用File Browser打造无下载文件管理体验
  • Ryujinx:C构建的Switch模拟器技术探索与实践指南
  • 5个简单步骤:用Rainmeter打造你的Windows个性化桌面终极指南
  • 别再死记硬背了!从‘极客大挑战’这道题,彻底搞懂PHP文件包含漏洞的过滤与绕过
  • 基于DP_MPC算法的氢能源动力无人机复合电源能量管理策略研究
  • 2026年4月国内评价高的焦炉横拉条厂家推荐,破碎机锤头/刀边腹板/上升管水封座盖/桥管,焦炉横拉条直销厂家哪个好 - 品牌推荐师
  • Phi-4-mini-reasoning一键部署教程:基于Ubuntu系统的快速环境搭建
  • LongCat动物百变秀应用:宠物创意照、趣味头像、社交配图一键生成
  • OpCore Simplify:三步零基础搞定黑苹果EFI配置的终极指南
  • 别再手动描边了!用LabelMe/CVAT高效搞定实例分割数据集标注(附避坑清单)
  • 如何快速上手EmotiVoice:2000+情感语音的终极免费TTS解决方案
  • MiniCPM-o-4.5-nvidia-FlagOS与Claude对比:在创意写作与逻辑推理任务上的表现
  • 2026年4月最新版地址电话查询:上海百达翡丽售后维修服务中心全指南 - 速递信息
  • MAA助手跨平台部署指南:从新手到专家的实践之路
  • 5个维度提升远程管理效率:MobaXterm中文版全攻略
  • STM32开发中SRAM与FLASH调试模式对比与优化
  • KOReader:打造个性化阅读解决方案从入门到精通
  • OpCore-Simplify:智能自动化EFI构建实战指南(2024)
  • 开源可部署+多场景落地:internlm2-chat-1.8b支撑政务问答、社区服务、热线助手
  • Burnside 引理与 Polya 定理
  • 掌握日期选择艺术:Bootstrap Datepicker 完全指南
  • 从单节点到集群:手把手教你用MinIO Operator v6.0.3动态扩展K8s存储租户(附扩容脚本)
  • AltDrag终极指南:一键改变Windows窗口操作体验的革命性工具
  • 3个关键策略掌握Plus Jakarta Sans:现代字体在技术项目中的实战应用