[链表] - 代码随想录 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个节点、偶数个节点、奇数个节点)都能被优雅处理,是链表交换问题的「标准解法」。

相关推荐
Python_Study20252 小时前
制造业企业如何构建高效数据采集系统:从挑战到实践
大数据·网络·数据结构·人工智能·架构
lixinnnn.2 小时前
优先级队列:最小函数值
数据结构·算法
Xの哲學2 小时前
Linux Worklet 深入剖析: 轻量级延迟执行机制
linux·服务器·网络·数据结构·算法
666HZ6662 小时前
数据结构2.1 线性表习题
c语言·数据结构·算法
yangminlei2 小时前
MySQL玩转数据可视化
数据结构·sql·oracle
CodeByV3 小时前
【算法题】链表
数据结构·算法
wen__xvn3 小时前
代码随想录算法训练营DAY15第六章 二叉树part03
数据结构·算法·leetcode
有一个好名字3 小时前
力扣-链表最大孪生和
算法·leetcode·链表
无限进步_4 小时前
【C语言&数据结构】二叉树链式结构完全指南:从基础到进阶
c语言·开发语言·数据结构·c++·git·算法·visual studio