[链表] - 代码随想录 24. 两两交换链表中的节点

[链表] - 代码随想录 24. 两两交换链表中的节点

题目要求

给定一个单链表,需两两交换相邻节点 ,并返回交换后的链表头节点。
关键约束 :不能仅修改节点的val值,必须通过调整节点的指针关系完成交换(即实际交换节点位置)。

解题思路:虚拟头节点法

链表问题的核心痛点是头节点的特殊处理(如空链表、仅1个节点),而**虚拟头节点(Dummy Node)**是解决这类问题的「银弹」------通过创建一个指向原头节点的虚拟节点,将所有节点的处理逻辑统一。

本题的核心逻辑可总结为「追踪前驱→标记节点→三步交换→移动指针」:

  1. 初始化指针
    • 创建dummy节点(值为0),指向原链表头head,避免头节点的特殊判断;
    • pre指针追踪每一对节点的前驱 (初始指向dummy),用于连接交换后的节点对。
  2. 循环条件 :当pre的下一个节点和下下个节点都存在时(即还有可交换的节点对),才进行交换。
  3. 三步交换 (核心!):
    假设当前要交换的节点对是curpre.next)和nxtpre.next.next),通过以下三步完成交换:
    • pre直接指向nxt(让前驱连接第二节点,跳过第一节点);
    • cur指向nxt.next(保存后续链表,避免断链);
    • nxt指向cur(完成交换,第二节点变为当前对的第一个节点)。
  4. 移动指针 :交换完成后,将pre移动到cur(当前对的第二个节点),为下一对节点的交换做准备。

实现代码(Java,带详细注释)

java 复制代码
/**
 * 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 swapPairs(ListNode head) {
        // 1. 虚拟头节点:指向原链表头,统一所有节点的处理逻辑
        ListNode dummy = new ListNode(0, head);
        // pre指针:追踪当前要交换的节点对的「前驱」(初始指向dummy)
        ListNode pre = dummy;

        // 2. 循环条件:还有至少两个节点可交换(pre.next和pre.next.next都不为空)
        while (pre.next != null && pre.next.next != null) {
            // 标记当前要交换的两个节点:cur(第一节点)、nxt(第二节点)
            ListNode cur = pre.next;      // pre的下一个节点(交换前的第一节点)
            ListNode nxt = pre.next.next; // pre的下下个节点(交换前的第二节点)

            // 3. 三步交换:核心逻辑,调整指针关系
            pre.next = nxt;       // 步骤1:前驱连接第二节点(pre → nxt)
            cur.next = nxt.next;  // 步骤2:第一节点连接后续链表(cur → nxt.next)
            nxt.next = cur;       // 步骤3:第二节点连接第一节点(nxt → cur)

            // 4. 移动pre指针:到当前对的第二节点(交换后的cur),为下一对做准备
            pre = cur;
        }

        // 返回交换后的链表头:虚拟头节点的下一个节点(dummy.next)
        return dummy.next;
    }
}

复杂度分析

  • 时间复杂度:O(n)。仅遍历链表一次,每个节点最多被访问两次(交换时的指针调整)。
  • 空间复杂度 :O(1)。仅用常数个额外指针(dummyprecurnxt),无额外空间开销。

关键说明

  • 虚拟头节点的价值 :将「头节点交换」与「中间节点交换」的逻辑统一,避免写if (head == null || head.next == null)的特殊判断。
  • pre指针的作用 :始终指向当前要交换节点对的前一个节点 ,确保交换后能重新连接链表(比如交换第1、2节点后,pre要移动到第2节点,才能处理第3、4节点)。

通过这种方法,所有边界情况(如空链表、1个节点、偶数个节点、奇数个节点)都能被优雅处理,是链表交换问题的「标准解法」。

相关推荐
田梓燊1 小时前
2026/4/11 leetcode 3741
数据结构·算法·leetcode
葳_人生_蕤1 小时前
hot100——栈和队列
数据结构
历程里程碑3 小时前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua
Meme Buoy4 小时前
18.补充数学1:生成树-最短路径-最大流量-线性规划
数据结构·算法
汀、人工智能4 小时前
[特殊字符] 第89课:岛屿数量
数据结构·算法·数据库架构·图论·bfs·岛屿数量
九英里路4 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串
2401_892070984 小时前
顺序栈(动态数组实现) 超详细解析(C++ 语言 + 可直接运行)
数据结构·c++·顺序栈
漫霂5 小时前
二叉树的翻转
java·数据结构·算法
3秒一个大5 小时前
深入理解 JS 中的栈与堆:从内存模型到数据结构,再谈内存泄漏
前端·javascript·数据结构
旖-旎6 小时前
哈希表(存在重复元素)(3)
数据结构·c++·学习·算法·leetcode·散列表