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

相关推荐
Queenie_Charlie8 小时前
前缀和的前缀和
数据结构·c++·树状数组
念越12 小时前
数据结构:栈堆
java·开发语言·数据结构
dear_bi_MyOnly12 小时前
【多线程——线程状态与安全】
java·开发语言·数据结构·后端·中间件·java-ee·intellij-idea
浪客灿心12 小时前
list_stack_queue
数据结构·list
zh_xuan12 小时前
最小跳跃次数
数据结构·算法
zh_xuan13 小时前
单青蛙跳台阶
数据结构·算法
罗湖老棍子14 小时前
【 例 1】石子合并(信息学奥赛一本通- P1569)
数据结构·算法·区间dp·区间动态规划·分割合并
小高Baby@14 小时前
JSON、bind、form
数据结构·json
TracyCoder12316 小时前
LeetCode Hot100(21/100)——234. 回文链表
算法·leetcode·链表
数智工坊17 小时前
【数据结构-栈】3.1栈的顺序存储-链式存储
java·开发语言·数据结构