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

相关推荐
努力努力再努力wz19 分钟前
【MySQL进阶系列】一文打通事务机制:从锁、Undo Log 到 MVCC 与隔离级别
c语言·数据结构·数据库·c++·mysql·算法·github
薇茗22 分钟前
【初阶数据结构】 左右逢源的分支诗律 二叉树1
c语言·数据结构·算法
澈20724 分钟前
C++ string全面解析:从入门到精通
数据结构·c++·算法
Irissgwe1 小时前
算法之滑动窗口
数据结构·算法
纽扣6671 小时前
【算法进阶之路】链表核心:快慢指针与反转链表专题精讲
数据结构·c++·算法·链表
浅念-1 小时前
吃透栈:LeetCode 栈算法题全解析
数据结构·c++·算法·leetcode·职场和发展·
承渊政道1 小时前
【动态规划算法】(两个数组的DP问题深度剖析与求解方法)
数据结构·c++·学习·算法·leetcode·动态规划·哈希算法
Hhy_11072 小时前
【从零开始学习数据结构 ④】:栈 ——后进先出的艺术
c语言·数据结构·学习·visual studio
海清河晏1112 小时前
数据结构 | 链式队列
开发语言·数据结构·链表
ulias2123 小时前
leetcode热题 - 5
数据结构·算法·leetcode