news 2026/9/5 6:20:02

两两交换链表中的节点:链表基础与递归思路详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
两两交换链表中的节点:链表基础与递归思路详解

1. 引言

在 LeetCode 的经典题目中,「两两交换链表中的节点」(Swap Nodes in Pairs)是一道非常能考察链表基本功和递归思维的题目。很多初学者在面对这道题时,往往会被指针的来回指向绕晕。本文将从链表的基本知识讲起,逐步深入到递归方法的基本思路,最后给出完整的代码实现,帮助你彻底吃透这道题。

2. 链表的基本知识

2.1 什么是链表

链表(Linked List)是一种线性数据结构,它通过「指针」将一系列节点串联起来。与数组不同,链表在内存中并不需要连续的空间,每个节点除了存储自身的数据(val)之外,还要存储指向下一个节点的指针(next)。

publicclassListNode{intval;ListNodenext;ListNode(){}ListNode(intval){this.val=val;}ListNode(intval,ListNodenext){this.val=val;this.next=next;}}

2.2 链表的核心特点

  • 非连续存储:节点在内存中分散存放,通过指针连接。
  • 动态大小:链表可以随时增删节点,不需要像数组那样预先分配固定容量。
  • 插入/删除高效:在已知前驱节点的情况下,插入和删除操作的时间复杂度为 O(1)。
  • 随机访问低效:要访问第 k 个节点,必须从头节点开始逐个遍历,时间复杂度为 O(n)。

2.3 链表的遍历

链表的遍历非常简单,核心就是不断移动cur指针:

ListNodecur=head;while(cur!=null){// 处理当前节点System.out.println(cur.val);// 移动到下一个节点cur=cur.next;}

2.4 为什么链表题容易出错

链表题出错的高频原因主要有两个:

  1. 指针丢失:修改next指向时,如果没有先用临时变量保存原指针,就会导致后续节点无法访问。
  2. 边界条件:空链表(head == null)、只有一个节点(head.next == null)等特殊情况没有处理好。

3. 题目理解:两两交换链表中的节点

3.1 题目描述

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

示例:

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

3.2 题目要点

  • 两两一组进行交换,即第 1 个和第 2 个交换,第 3 个和第 4 个交换,以此类推。
  • 如果链表长度是奇数,最后一个节点保持不动。
  • 只能交换节点本身,不能只交换节点里的值。

4. 递归方法的基本思路

4.1 什么是递归

递归(Recursion)是一种通过「函数调用自身」来解决问题的方法。一个递归问题通常包含两个核心要素:

  1. 递归基(Base Case):问题规模最小、可以直接返回答案的情况,用于终止递归。
  2. 递归关系(Recursive Relation):把大问题拆解成规模更小的同类子问题,并建立它们之间的联系。

4.2 递归的思考方式

面对递归问题,不要试图在脑子里把每一层调用都展开。正确的思考方式是:

  • 假设子问题已经解决:相信递归函数能正确处理规模更小的子问题。
  • 只关心当前层要做什么:当前层只需要处理「本层」的逻辑,剩下的交给递归。

4.3 用递归思考「两两交换」

我们以链表1 -> 2 -> 3 -> 4为例,思考如何用递归解决:

第一步:找递归基

  • 如果链表为空(head == null),或者只有一个节点(head.next == null),无法进行交换,直接返回head

第二步:拆解子问题

  • 对于链表1 -> 2 -> 3 -> 4,我们先把前两个节点12拿出来。
  • 剩下的链表3 -> 4是一个规模更小的同类问题,我们相信递归函数swapPairs(3)能把它正确交换成4 -> 3

第三步:处理当前层

  • 当前层要做的就是把12交换位置,并把交换后的结果与子问题的结果连接起来:
    • 2.next = 1
    • 1.next = swapPairs(3)(即4 -> 3
  • 最终得到2 -> 1 -> 4 -> 3

4.4 递归代码实现

publicListNodeswapPairs(ListNodehead){// 递归基:空链表或只有一个节点,无法交换if(head==null||head.next==null){returnhead;}// 保存第二个节点ListNodenewHead=head.next;// 递归处理剩余部分:head.next 指向交换后的子链表head.next=swapPairs(newHead.next);// 第二个节点指向第一个节点,完成交换newHead.next=head;// 返回新的头节点returnnewHead;}

4.5 递归过程图解

swapPairs(1->2->3->4)

newHead = 2

head.next = swapPairs(3->4)

swapPairs(3->4) 返回 4->3

head.next = 4->3

newHead.next = head

返回 2->1->4->3

4.6 时间复杂度与空间复杂度

  • 时间复杂度:O(n),每个节点只被访问一次。
  • 空间复杂度:O(n),递归调用栈的深度为 n/2,即 O(n)。

5. 迭代方法(补充)

除了递归,这道题也可以用迭代的方式解决,通过引入一个虚拟头节点(dummy node)来简化边界处理:

publicListNodeswapPairs(ListNodehead){ListNodedummy=newListNode(0);dummy.next=head;ListNodeprev=dummy;while(prev.next!=null&&prev.next.next!=null){ListNodefirst=prev.next;ListNodesecond=first.next;// 交换两个节点first.next=second.next;second.next=first;prev.next=second;// 移动 prev 到下一组的前驱prev=first;}returndummy.next;}

迭代方法的时间复杂度同样是 O(n),但空间复杂度优化到了 O(1)。

6. 总结

「两两交换链表中的节点」是一道非常经典的链表递归题。通过这道题,我们重点掌握了:

  1. 链表的基本结构:节点由valnext组成,遍历靠移动指针。
  2. 递归的核心思路:先找递归基,再拆解子问题,最后处理当前层。
  3. 递归代码的写法:相信子问题已解决,只关心当前层的指针调整。

建议读者在理解递归思路后,再动手实现一遍迭代版本,对比两种方法的异同,这样对链表的理解会更加深刻。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 6:19:37

交互式数字内容项目技术解析:部署、测试与性能评估指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:19:05

外贸SEO与传统展会结合:让展会上认识的人主动Google搜你

做了七八年外贸,我养成了一个习惯:展会结束后第一周不做别的,先更新网站内容。把展会上客户问得最多的那几个问题,变成产品页或文章补上去。两个月后统计发现,那些在展会上交换过名片的人,有相当一部分会再…

作者头像 李华
网站建设 2026/9/5 6:19:02

本地大模型部署实战:参数、显存与量化如何匹配?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:17:17

【AI使用说明】2026数学建模国赛历年优秀获奖论文+论文写作模板

序言 文档第1、2页为内容说明页,使用时可以进行删除 国赛乃至大部分数模竞赛从来没有给出任何的论文模版,大部分的模版均为一次又一次学生、老师内部传播形成。本文档结合竞赛格式规范、网络资料、各高校内部资料编写形成。以下为国赛最新的格式规范&a…

作者头像 李华
网站建设 2026/9/5 6:15:40

ESP-IDF报错INTR_CPU_ID_AUTO未定义?版本兼容性排查与解决方案

1. 问题现象与影响范围先说一下这个报错长什么样。你从 GitHub 拉了一个新项目的 demo,或者照着某篇教程的代码写了外设中断初始化,用 VS Code 的 ESP-IDF 插件编译,终端里突然蹦出来一堆红色报错,核心内容大概是:erro…

作者头像 李华
网站建设 2026/9/5 6:13:59

开源项目吐槽大会:从「用爱发电」到「用命踩坑」

凌晨两点,我盯着屏幕上那行刺眼的报错,第 17 次刷新了 GitHub Issue 页面——依然没有回复。三天前,我满怀信心地把一个开源库集成进项目,照着 README 的示例代码敲了一遍,结果 parse() 一调用就抛 SyntaxError。我以为…

作者头像 李华