Java详解LeetCode 热题 100(23):LeetCode 206. 反转链表(Reverse Linked List)详解

1. 题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

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

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

进阶: 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

1.1 链表节点定义

/**
 * 单链表节点的定义
 */
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; 
    }
}

2. 理解题目

这道题要求我们反转一个单链表。具体来说:

  • 输入是一个链表的头节点 head
  • 需要将链表中所有节点的指针方向反转
  • 返回新的头节点(原来的尾节点)

关键点:

  1. 链表的方向需要完全颠倒
  2. 原来的头节点将变成尾节点,原来的尾节点将变成头节点
  3. 每个节点的 next 指针都要改变方向
  4. 需要处理空链表和单节点链表的特殊情况

2.1 反转前后对比

反转前:

1 -> 2 -> 3 -> 4 -> 5 -> NULL

反转后:

NULL <- 1 <- 2 <- 3 <- 4 <- 5

实际上就是:

5 -> 4 -> 3 -> 2 -> 1 -> NULL

2.2 核心思路

反转链表的关键在于:

  1. 改变指针方向:将每个节点的 next 指针从指向后一个节点改为指向前一个节点
  2. 保存节点信息:在改变指针之前,必须先保存下一个节点的信息,否则会丢失链表的后续部分
  3. 逐步处理:从头到尾遍历链表,逐个处理每个节点

3. 解法一:迭代法(三指针法)

3.1 算法思路

迭代法是最直观、最容易理解的方法。我们使用三个指针:

  • prev:指向当前节点的前一个节点(反转后的下一个节点)
  • curr:指向当前正在处理的节点
  • next:临时保存当前节点的下一个节点,防止丢失

核心步骤:

  1. 初始化 prev = nullcurr = head
  2. 循环处理每个节点:
    • 保存 curr.nextnext
    • curr.next 指向 prev(反转指针)
    • prevcurr 都向前移动一步
  3. 返回 prev(新的头节点)

3.2 详细图解

让我们通过图解来理解每一步:

初始状态:

prev = NULL
curr = 1 -> 2 -> 3 -> 4 -> 5 -> NULL
next = ?

第一步:处理节点1

1. next = curr.next = 2 -> 3 -> 4 -> 5 -> NULL  (保存下一个节点)
2. curr.next = prev = NULL                       (反转指针)
3. prev = curr = 1                               (移动prev)
4. curr = next = 2 -> 3 -> 4 -> 5 -> NULL       (移动curr)

结果:NULL <- 1    2 -> 3 -> 4 -> 5 -> NULL
            prev  curr

第二步:处理节点2

1. next = curr.next = 3 -> 4 -> 5 -> NULL
2. curr.next = prev = 1
3. prev = curr = 2
4. curr = next = 3 -> 4 -> 5 -> NULL

结果:NULL <- 1 <- 2    3 -> 4 -> 5 -> NULL
                 prev  curr

继续这个过程直到 curr 为 NULL…

3.3 Java代码实现

/**
 * 迭代法反转链表(三指针法)
 * 时间复杂度:O(n),其中 n 是链表的长度,需要遍历链表一次
 * 空间复杂度:O(1),只使用常数级额外空间
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        // 边界条件:空链表或只有一个节点
        if (head == null || head.next == null) {
            return head;
        }
        
        // 初始化三个指针
        ListNode prev = null;    // 前一个节点,初始为null
        ListNode curr = head;    // 当前节点,从头节点开始
        ListNode next = null;    // 下一个节点,用于临时保存
        
        // 遍历链表,逐个反转节点
        while (curr != null) {
            // 步骤1:保存下一个节点,防止丢失链表后续部分
            next = curr.next;
            
            // 步骤2:反转当前节点的指针,指向前一个节点
            curr.next = prev;
            
            // 步骤3:移动指针,为下一次迭代做准备
            prev = curr;         // prev移动到当前节点
            curr = next;         // curr移动到下一个节点
        }
        
        // 循环结束时,prev指向原链表的最后一个节点,即新链表的头节点
        return prev;
    }
}

3.4 代码执行过程演示

让我们用示例 [1,2,3,4,5] 来详细演示代码执行过程:

/**
 * 迭代过程详细演示类
 */
public class IterativeReverseDemo {
    public ListNode reverseList(ListNode head) {
        System.out.println("=== 开始反转链表 ===");
        System.out.println("原链表:" + printList(head));
        
        if (head == null || head.next == null) {
            System.out.println("链表为空或只有一个节点,直接返回");
            return head;
        }
        
        ListNode prev = null;
        ListNode curr = head;
        ListNode next = null;
        int step = 1;
        
        while (curr != null) {
            System.out.println("\n--- 第" + step + "步 ---");
            System.out.println("当前状态:");
            System.out.println("  prev: " + (prev == null ? "null" : prev.val));
            System.out.println("  curr: " + curr.val);
            System.out.println("  next: " + (curr.next == null ? "null" : curr.next.val));
            
            // 保存下一个节点
            next = curr.next;
            System.out.println("保存下一个节点:next = " + (next == null ? "null" : next.val));
            
            // 反转指针
            curr.next = prev;
            System.out.println("反转指针:" + curr.val + ".next = " + (prev == null ? "null" : prev.val));
            
            // 移动指针
            prev = curr;
            curr = next;
            System.out.println("移动指针:");
            System.out.println("  prev = " + prev.val);
            System.out.println("  curr = " + (curr == null ? "null" : curr.val));
            
            // 打印当前已反转部分
            System.out.println("当前已反转部分:" + printReversedPart(prev, curr));
            
            step++;
        }
        
        System.out.println("\n=== 反转完成 ===");
        System.out.println("最终结果:" + printList(prev));
        return prev;
    }
    
    // 辅助方法:打印链表
    private String printList(ListNode head) {
        if (head == null) return "[]";
        
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        while (head != null) {
            sb.append(head.val);
            if (head.next != null) sb.append(" -> ");
            head = head.next;
        }
        sb.append(" -> null]");
        return sb.toString();
    }
    
    // 辅助方法:打印已反转部分
    private String printReversedPart(ListNode reversed, ListNode remaining) {
        StringBuilder sb = new StringBuilder();
        sb.append("已反转:");
        
        if (reversed == null) {
            sb.append("null");
        } else {
            // 需要从前往后打印已反转部分,但我们只有指向最后一个的指针
            // 为了演示,我们简化显示
            sb.append("... -> ").append(reversed.val).append(" -> null");
        }
        
        sb.append(" | 待处理:");
        if (remaining == null) {
            sb.append("null");
        } else {
            ListNode temp = remaining;
            sb.append("[");
            while (temp != null) {
                sb.append(temp.val);
                if (temp.next != null) sb.append(" -> ");
                temp = temp.next;
            }
            sb.append(" -> null]");
        }
        
        return sb.toString();
    }
}

3.5 执行结果示例

=== 开始反转链表 ===
原链表:[1 -> 2 -> 3 -> 4 -> 5 -> null]

--- 第1步 ---
当前状态:
  prev: null
  curr: 1
  next: 2
保存下一个节点:next = 2
反转指针:1.next = null
移动指针:
  prev = 1
  curr = 2
当前已反转部分:已反转:... -> 1 -> null | 待处理:[2 -> 3 -> 4 -> 5 -> null]

--- 第2步 ---
当前状态:
  prev: 1
  curr: 2
  next: 3
保存下一个节点:next = 3
反转指针:2.next = 1
移动指针:
  prev = 2
  curr = 3
当前已反转部分:已反转:... -> 2 -> null | 待处理:[3 -> 4 -> 5 -> null]

--- 第3步 ---
当前状态:
  prev: 2
  curr: 3
  next: 4
保存下一个节点:next = 4
反转指针:3.next = 2
移动指针:
  prev = 3
  curr = 4
当前已反转部分:已反转:... -> 3 -> null | 待处理:[4 -> 5 -> null]

--- 第4步 ---
当前状态:
  prev: 3
  curr: 4
  next: 5
保存下一个节点:next = 5
反转指针:4.next = 3
移动指针:
  prev = 4
  curr = 5
当前已反转部分:已反转:... -> 4 -> null | 待处理:[5 -> null]

--- 第5步 ---
当前状态:
  prev: 4
  curr: 5
  next: null
保存下一个节点:next = null
反转指针:5.next = 4
移动指针:
  prev = 5
  curr = null
当前已反转部分:已反转:... -> 5 -> null | 待处理:null

=== 反转完成 ===
最终结果:[5 -> 4 -> 3 -> 2 -> 1 -> null]

3.6 优化版本(简化代码)

/**
 * 迭代法的简洁版本
 * 功能完全相同,但代码更简洁
 */
class SolutionOptimized {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        
        while (curr != null) {
            ListNode next = curr.next;  // 保存下一个节点
            curr.next = prev;           // 反转指针
            prev = curr;                // 移动prev
            curr = next;                // 移动curr
        }
        
        return prev;  // prev指向新的头节点
    }
}

3.7 复杂度分析

时间复杂度: O(n)

  • 其中 n 是链表的长度
  • 我们需要遍历链表一次,每个节点只访问一次
  • 每次操作都是常数时间

空间复杂度: O(1)

  • 只使用了常数级的额外空间(三个指针变量)
  • 不依赖于输入链表的长度

3.8 适用场景

迭代法是解决链表反转问题的首选方法,因为:

  1. 空间效率高:只使用常数级额外空间
  2. 易于理解:逻辑直观,容易掌握
  3. 性能优秀:没有递归调用开销
  4. 适用性广:适合处理长链表,不会出现栈溢出问题

4. 解法二:递归法

4.1 递归思路

递归的核心思想是将大问题分解为小问题:

  1. 递归假设:假设 reverseList(head.next) 能够正确反转从第二个节点开始的子链表
  2. 处理当前节点:将当前节点接到已反转的子链表末尾
  3. 返回新头节点:返回反转后链表的头节点

递归过程分析:
对于链表 1 -> 2 -> 3 -> 4 -> 5 -> null

reverseList(1->2->3->4->5)
├── 递归处理子链表:reverseList(2->3->4->5) 返回 5->4->3->2
├── 将当前节点1接到反转后的链表末尾:5->4->3->2->1
└── 返回新的头节点5

最终结果:5->4->3->2->1->null

4.2 Java递归实现

/**
 * 递归法反转链表
 * 时间复杂度:O(n),其中 n 是链表的长度
 * 空间复杂度:O(n),递归调用栈的深度为 n
 */
class RecursiveSolution {
    public ListNode reverseList(ListNode head) {
        // 递归终止条件:空链表或单节点链表
        if (head == null || head.next == null) {
            return head;
        }
        
        // 递归反转子链表,获得新的头节点
        ListNode newHead = reverseList(head.next);
        
        // 反转当前节点和下一个节点的连接
        // head.next 现在指向子链表的第一个节点(反转前的第二个节点)
        // 我们让这个节点指向当前节点
        head.next.next = head;
        
        // 断开当前节点到下一个节点的连接,避免形成环
        head.next = null;
        
        // 返回新的头节点(整个递归过程中保持不变)
        return newHead;
    }
}

4.3 递归过程详细演示

/**
 * 递归过程可视化演示
 */
public class RecursiveReverseDemo {
    private int depth = 0;  // 递归深度计数器
    
    public ListNode reverseList(ListNode head) {
        String indent = "  ".repeat(depth);  // 缩进显示递归层次
        
        System.out.println(indent + "→ 进入递归 depth=" + depth + 
                          ", 当前节点: " + (head == null ? "null" : head.val));
        
        // 递归终止条件
        if (head == null || head.next == null) {
            System.out.println(indent + "← 递归边界,返回: " + 
                              (head == null ? "null" : head.val));
            return head;
        }
        
        System.out.println(indent + "当前处理: " + head.val + " -> " + 
                          (head.next == null ? "null" : head.next.val));
        
        depth++;  // 递归深度增加
        
        // 递归处理子链表
        System.out.println(indent + "递归处理子链表...");
        ListNode newHead = reverseList(head.next);
        
        depth--;  // 递归深度减少
        
        System.out.println(indent + "← 递归返回,继续处理节点: " + head.val);
        System.out.println(indent + "子链表已反转,新头节点: " + newHead.val);
        
        // 反转当前节点和下一个节点的连接
        System.out.println(indent + "反转连接: " + head.val + " <-> " + head.next.val);
        System.out.println(indent + "执行: " + head.next.val + ".next = " + head.val);
        head.next.next = head;
        
        System.out.println(indent + "断开: " + head.val + ".next = null");
        head.next = null;
        
        System.out.println(indent + "当前层处理完成,返回头节点: " + newHead.val);
        
        return newHead;
    }
}

4.4 递归执行过程

对于输入 [1,2,3,4,5],递归过程如下:

→ 进入递归 depth=0, 当前节点: 1
当前处理: 1 -> 2
递归处理子链表...
  → 进入递归 depth=1, 当前节点: 2
  当前处理: 2 -> 3
  递归处理子链表...
    → 进入递归 depth=2, 当前节点: 3
    当前处理: 3 -> 4
    递归处理子链表...
      → 进入递归 depth=3, 当前节点: 4
      当前处理: 4 -> 5
      递归处理子链表...
        → 进入递归 depth=4, 当前节点: 5
        ← 递归边界,返回: 5
      ← 递归返回,继续处理节点: 4
      子链表已反转,新头节点: 5
      反转连接: 4 <-> 5
      执行: 5.next = 4
      断开: 4.next = null
      当前层处理完成,返回头节点: 5
    ← 递归返回,继续处理节点: 3
    子链表已反转,新头节点: 5
    反转连接: 3 <-> 4
    执行: 4.next = 3
    断开: 3.next = null
    当前层处理完成,返回头节点: 5
  ← 递归返回,继续处理节点: 2
  子链表已反转,新头节点: 5
  反转连接: 2 <-> 3
  执行: 3.next = 2
  断开: 2.next = null
  当前层处理完成,返回头节点: 5
← 递归返回,继续处理节点: 1
子链表已反转,新头节点: 5
反转连接: 1 <-> 2
执行: 2.next = 1
断开: 1.next = null
当前层处理完成,返回头节点: 5

4.5 递归的图解说明

原链表:1 -> 2 -> 3 -> 4 -> 5 -> null

递归调用栈展开:
reverseList(1->2->3->4->5) {
    newHead = reverseList(2->3->4->5) {
        newHead = reverseList(3->4->5) {
            newHead = reverseList(4->5) {
                newHead = reverseList(5) {
                    return 5;  // 递归出口
                }
                // 处理节点4
                // 当前状态:5  4->5
                4.next.next = 4;  // 5.next = 4
                4.next = null;    // 断开 4->5
                // 结果:5->4  (4指向null)
                return 5;
            }
            // 处理节点3
            // 当前状态:5->4  3->4
            3.next.next = 3;  // 4.next = 3
            3.next = null;    // 断开 3->4
            // 结果:5->4->3  (3指向null)
            return 5;
        }
        // 处理节点2
        // 当前状态:5->4->3  2->3
        2.next.next = 2;  // 3.next = 2
        2.next = null;    // 断开 2->3
        // 结果:5->4->3->2  (2指向null)
        return 5;
    }
    // 处理节点1
    // 当前状态:5->4->3->2  1->2
    1.next.next = 1;  // 2.next = 1
    1.next = null;    // 断开 1->2
    // 结果:5->4->3->2->1  (1指向null)
    return 5;
}

最终结果:5 -> 4 -> 3 -> 2 -> 1 -> null

4.6 递归算法的关键理解

  1. 递归假设的重要性

    • 我们假设 reverseList(head.next) 能正确反转子链表
    • 基于这个假设,我们只需要处理当前节点与子链表的连接
  2. "反转"的具体操作

    head.next.next = head;  // 让下一个节点指向当前节点
    head.next = null;       // 断开当前节点到下一个节点的连接
    
  3. 返回值的传递

    • 新的头节点(原链表的尾节点)在整个递归过程中保持不变
    • 每层递归都返回这个相同的头节点

4.7 复杂度分析

时间复杂度: O(n)

  • 每个节点被访问一次,总共 n 次递归调用
  • 每次递归的操作都是常数时间

空间复杂度: O(n)

  • 递归调用栈的深度为 n(链表长度)
  • 每层递归需要常数级的额外空间
  • 总空间复杂度为 O(n)

4.8 递归法的优缺点

优点:

  1. 代码简洁:递归版本的代码更加简洁优雅
  2. 思路清晰:递归思维符合问题的分解特性
  3. 易于理解:一旦理解递归思想,代码逻辑很清晰

缺点:

  1. 空间开销大:需要 O(n) 的递归栈空间
  2. 可能栈溢出:对于很长的链表,可能导致栈溢出
  3. 性能略差:函数调用有一定开销

5. 完整测试用例和边界处理

5.1 完整测试代码

/**
 * 反转链表完整测试类
 */
public class ReverseListTest {
    
    public static void main(String[] args) {
        ReverseListTest test = new ReverseListTest();
        
        System.out.println("=== 反转链表测试套件 ===\n");
        
        // 运行所有测试用例
        test.testCase1_NormalList();
        test.testCase2_EmptyList();
        test.testCase3_SingleNode();
        test.testCase4_TwoNodes();
        test.testCase5_LargeList();
        test.testCase6_PerformanceTest();
        
        System.out.println("=== 所有测试完成 ===");
    }
    
    /**
     * 测试用例1:正常链表 [1,2,3,4,5]
     */
    public void testCase1_NormalList() {
        System.out.println("【测试用例1】正常链表 [1,2,3,4,5]");
        
        // 构建测试链表
        ListNode head = buildList(new int[]{1, 2, 3, 4, 5});
        System.out.println("原链表:" + printList(head));
        
        // 测试迭代法
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(copyList(head));
        System.out.println("迭代法结果:" + printList(result1));
        
        // 测试递归法
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(copyList(head));
        System.out.println("递归法结果:" + printList(result2));
        
        // 验证结果
        boolean isCorrect = isEqual(result1, result2) && 
                           isListEqual(result1, new int[]{5, 4, 3, 2, 1});
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    /**
     * 测试用例2:空链表 []
     */
    public void testCase2_EmptyList() {
        System.out.println("【测试用例2】空链表 []");
        
        ListNode head = null;
        System.out.println("原链表:" + printList(head));
        
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(head);
        System.out.println("迭代法结果:" + printList(result1));
        
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(head);
        System.out.println("递归法结果:" + printList(result2));
        
        boolean isCorrect = (result1 == null && result2 == null);
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    /**
     * 测试用例3:单节点链表 [42]
     */
    public void testCase3_SingleNode() {
        System.out.println("【测试用例3】单节点链表 [42]");
        
        ListNode head = new ListNode(42);
        System.out.println("原链表:" + printList(head));
        
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(copyList(head));
        System.out.println("迭代法结果:" + printList(result1));
        
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(copyList(head));
        System.out.println("递归法结果:" + printList(result2));
        
        boolean isCorrect = isEqual(result1, result2) && 
                           result1 != null && result1.val == 42 && result1.next == null;
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    /**
     * 测试用例4:两个节点 [1,2]
     */
    public void testCase4_TwoNodes() {
        System.out.println("【测试用例4】两个节点 [1,2]");
        
        ListNode head = buildList(new int[]{1, 2});
        System.out.println("原链表:" + printList(head));
        
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(copyList(head));
        System.out.println("迭代法结果:" + printList(result1));
        
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(copyList(head));
        System.out.println("递归法结果:" + printList(result2));
        
        boolean isCorrect = isEqual(result1, result2) && 
                           isListEqual(result1, new int[]{2, 1});
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    /**
     * 测试用例5:较大链表 [1,2,3,...,10]
     */
    public void testCase5_LargeList() {
        System.out.println("【测试用例5】较大链表 [1,2,3,4,5,6,7,8,9,10]");
        
        int[] values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        ListNode head = buildList(values);
        System.out.println("原链表:" + printList(head));
        
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(copyList(head));
        System.out.println("迭代法结果:" + printList(result1));
        
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(copyList(head));
        System.out.println("递归法结果:" + printList(result2));
        
        int[] expected = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
        boolean isCorrect = isEqual(result1, result2) && 
                           isListEqual(result1, expected);
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    /**
     * 测试用例6:性能测试
     */
    public void testCase6_PerformanceTest() {
        System.out.println("【测试用例6】性能测试 - 1000个节点");
        
        // 构建1000个节点的链表
        int[] values = new int[1000];
        for (int i = 0; i < 1000; i++) {
            values[i] = i + 1;
        }
        ListNode head = buildList(values);
        System.out.println("构建了包含1000个节点的链表");
        
        // 测试迭代法性能
        long start1 = System.nanoTime();
        Solution iterativeSol = new Solution();
        ListNode result1 = iterativeSol.reverseList(copyList(head));
        long end1 = System.nanoTime();
        long iterativeTime = end1 - start1;
        
        // 测试递归法性能
        long start2 = System.nanoTime();
        RecursiveSolution recursiveSol = new RecursiveSolution();
        ListNode result2 = recursiveSol.reverseList(copyList(head));
        long end2 = System.nanoTime();
        long recursiveTime = end2 - start2;
        
        System.out.println("迭代法耗时:" + iterativeTime + " 纳秒");
        System.out.println("递归法耗时:" + recursiveTime + " 纳秒");
        System.out.println("性能对比:递归法耗时是迭代法的 " + 
                          String.format("%.2f", (double) recursiveTime / iterativeTime) + " 倍");
        
        // 验证结果正确性
        boolean isCorrect = isEqual(result1, result2);
        System.out.println("结果验证:" + (isCorrect ? "✅ 通过" : "❌ 失败"));
        System.out.println();
    }
    
    // ===== 辅助方法 =====
    
    /**
     * 根据数组构建链表
     */
    private ListNode buildList(int[] values) {
        if (values == null || values.length == 0) {
            return null;
        }
        
        ListNode head = new ListNode(values[0]);
        ListNode curr = head;
        
        for (int i = 1; i < values.length; i++) {
            curr.next = new ListNode(values[i]);
            curr = curr.next;
        }
        
        return head;
    }
    
    /**
     * 打印链表
     */
    private String printList(ListNode head) {
        if (head == null) return "[]";
        
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        ListNode curr = head;
        while (curr != null) {
            sb.append(curr.val);
            if (curr.next != null) sb.append(", ");
            curr = curr.next;
        }
        sb.append("]");
        return sb.toString();
    }
    
    /**
     * 复制链表
     */
    private ListNode copyList(ListNode head) {
        if (head == null) return null;
        
        ListNode newHead = new ListNode(head.val);
        ListNode newCurr = newHead;
        ListNode curr = head.next;
        
        while (curr != null) {
            newCurr.next = new ListNode(curr.val);
            newCurr = newCurr.next;
            curr = curr.next;
        }
        
        return newHead;
    }
    
    /**
     * 比较两个链表是否相等
     */
    private boolean isEqual(ListNode l1, ListNode l2) {
        while (l1 != null && l2 != null) {
            if (l1.val != l2.val) return false;
            l1 = l1.next;
            l2 = l2.next;
        }
        return l1 == null && l2 == null;
    }
    
    /**
     * 检查链表是否与给定数组相等
     */
    private boolean isListEqual(ListNode head, int[] expected) {
        ListNode curr = head;
        for (int i = 0; i < expected.length; i++) {
            if (curr == null || curr.val != expected[i]) {
                return false;
            }
            curr = curr.next;
        }
        return curr == null;
    }
}

6. 常见错误与调试技巧

6.1 常见错误分析

错误1:忘记保存下一个节点
// ❌ 错误代码:会导致链表丢失
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    
    while (curr != null) {
        curr.next = prev;    // 直接修改next,丢失了后续节点!
        prev = curr;
        curr = curr.next;    // curr.next已经被修改,无法继续遍历
    }
    
    return prev;
}

// ✅ 正确代码:先保存再修改
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    
    while (curr != null) {
        ListNode next = curr.next;  // 先保存下一个节点
        curr.next = prev;           // 再修改指针
        prev = curr;
        curr = next;                // 使用保存的节点继续遍历
    }
    
    return prev;
}
错误2:返回错误的头节点
// ❌ 错误:返回原头节点
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    
    return head;  // 错误!head现在是尾节点,应该返回prev
}

// ✅ 正确:返回新的头节点
public ListNode reverseList(ListNode head) {
    // ... 相同的处理逻辑 ...
    return prev;  // prev指向新的头节点(原来的尾节点)
}
错误3:边界条件处理不当
// ❌ 错误:没有处理特殊情况
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    
    // 如果head为null,这里会出错
    while (curr != null) {
        // ...
    }
    
    return prev;
}

// ✅ 正确:先处理边界条件
public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;  // 空链表或单节点直接返回
    }
    
    // ... 正常处理逻辑 ...
}
错误4:递归中形成环
// ❌ 错误:没有断开原连接,形成环
public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    
    ListNode newHead = reverseList(head.next);
    head.next.next = head;  // 反转连接
    // 忘记断开原连接!会形成环:head <-> head.next
    
    return newHead;
}

// ✅ 正确:断开原连接
public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }
    
    ListNode newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;       // 必须断开原连接
    
    return newHead;
}

6.2 调试技巧

技巧1:添加调试输出
/**
 * 带调试输出的反转函数
 */
public ListNode reverseListWithDebug(ListNode head) {
    System.out.println("开始反转:" + printList(head));
    
    if (head == null || head.next == null) {
        System.out.println("边界情况,直接返回");
        return head;
    }
    
    ListNode prev = null;
    ListNode curr = head;
    int step = 0;
    
    while (curr != null) {
        System.out.println("步骤 " + (++step) + ":");
        System.out.println("  处理节点: " + curr.val);
        System.out.println("  当前状态: prev=" + (prev == null ? "null" : prev.val) + 
                          ", curr=" + curr.val);
        
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
        
        System.out.println("  操作后: prev=" + prev.val + 
                          ", curr=" + (curr == null ? "null" : curr.val));
        System.out.println("  当前已反转部分: " + printPartialList(prev, 3));
        System.out.println();
    }
    
    System.out.println("反转完成:" + printList(prev));
    return prev;
}

private String printPartialList(ListNode head, int maxNodes) {
    if (head == null) return "null";
    
    StringBuilder sb = new StringBuilder();
    ListNode curr = head;
    int count = 0;
    
    while (curr != null && count < maxNodes) {
        sb.append(curr.val);
        if (curr.next != null && count < maxNodes - 1) sb.append("->");
        curr = curr.next;
        count++;
    }
    
    if (curr != null) sb.append("->...");
    return sb.toString();
}
技巧2:单元测试验证
/**
 * 单元测试类
 */
public class ReverseListUnitTest {
    
    @Test
    public void testEmptyList() {
        Solution sol = new Solution();
        ListNode result = sol.reverseList(null);
        assertNull("空链表应返回null", result);
    }
    
    @Test
    public void testSingleNode() {
        Solution sol = new Solution();
        ListNode head = new ListNode(1);
        ListNode result = sol.reverseList(head);
        
        assertNotNull("单节点不应为null", result);
        assertEquals("值应该是1", 1, result.val);
        assertNull("下一个节点应为null", result.next);
    }
    
    @Test
    public void testTwoNodes() {
        Solution sol = new Solution();
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        
        ListNode result = sol.reverseList(head);
        
        assertEquals("第一个节点值应为2", 2, result.val);
        assertEquals("第二个节点值应为1", 1, result.next.val);
        assertNull("第三个节点应为null", result.next.next);
    }
    
    @Test
    public void testNormalList() {
        Solution sol = new Solution();
        ListNode head = buildList(new int[]{1, 2, 3, 4, 5});
        
        ListNode result = sol.reverseList(head);
        
        int[] expected = {5, 4, 3, 2, 1};
        for (int i = 0; i < expected.length; i++) {
            assertNotNull("节点不应为null", result);
            assertEquals("节点值不匹配", expected[i], result.val);
            result = result.next;
        }
        assertNull("最后应为null", result);
    }
}
技巧3:可视化工具
/**
 * 链表可视化工具
 */
public class ListVisualizer {
    
    /**
     * 生成链表的图形表示
     */
    public static void visualizeList(ListNode head, String title) {
        System.out.println("\n=== " + title + " ===");
        
        if (head == null) {
            System.out.println("null");
            return;
        }
        
        // 打印节点值
        ListNode curr = head;
        while (curr != null) {
            System.out.print("┌─────┐");
            if (curr.next != null) System.out.print("    ");
            curr = curr.next;
        }
        System.out.println();
        
        // 打印节点内容
        curr = head;
        while (curr != null) {
            System.out.printf("│  %2d │", curr.val);
            if (curr.next != null) System.out.print(" -> ");
            curr = curr.next;
        }
        System.out.println(" -> null");
        
        // 打印底部边框
        curr = head;
        while (curr != null) {
            System.out.print("└─────┘");
            if (curr.next != null) System.out.print("    ");
            curr = curr.next;
        }
        System.out.println("\n");
    }
    
    /**
     * 演示反转过程
     */
    public static void demonstrateReverse() {
        ListNode head = buildList(new int[]{1, 2, 3, 4, 5});
        
        visualizeList(head, "原始链表");
        
        Solution sol = new Solution();
        ListNode reversed = sol.reverseList(head);
        
        visualizeList(reversed, "反转后链表");
    }
}

7. 扩展题目与变种

7.1 反转链表 II (LeetCode 92)

给你单链表的头指针 head 和两个整数 leftright,其中 left <= right。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。

/**
 * 反转链表的指定区间
 */
public class ReverseListII {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null || left == right) {
            return head;
        }
        
        // 创建虚拟头节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        
        // 找到left位置的前一个节点
        ListNode prevLeft = dummy;
        for (int i = 1; i < left; i++) {
            prevLeft = prevLeft.next;
        }
        
        // 反转left到right之间的节点
        ListNode prev = null;
        ListNode curr = prevLeft.next;
        
        for (int i = left; i <= right; i++) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        // 重新连接
        prevLeft.next.next = curr;  // 连接反转部分的尾部
        prevLeft.next = prev;       // 连接反转部分的头部
        
        return dummy.next;
    }
}

7.2 两两交换链表中的节点 (LeetCode 24)

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

/**
 * 两两交换链表节点
 */
public class SwapPairs {
    
    /**
     * 迭代法
     */
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode prev = dummy;
        
        while (prev.next != null && prev.next.next != null) {
            // 要交换的两个节点
            ListNode first = prev.next;
            ListNode second = prev.next.next;
            
            // 交换
            prev.next = second;
            first.next = second.next;
            second.next = first;
            
            // 移动prev到下一组的前面
            prev = first;
        }
        
        return dummy.next;
    }
    
    /**
     * 递归法
     */
    public ListNode swapPairsRecursive(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        ListNode second = head.next;
        head.next = swapPairsRecursive(second.next);
        second.next = head;
        
        return second;
    }
}

7.3 K个一组翻转链表 (LeetCode 25)

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

/**
 * K个一组翻转链表
 */
public class ReverseKGroup {
    public ListNode reverseKGroup(ListNode head, int k) {
        if (head == null || k == 1) {
            return head;
        }
        
        // 检查是否有k个节点
        ListNode curr = head;
        for (int i = 0; i < k; i++) {
            if (curr == null) {
                return head;  // 不足k个节点,不反转
            }
            curr = curr.next;
        }
        
        // 反转前k个节点
        ListNode prev = null;
        curr = head;
        for (int i = 0; i < k; i++) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        // 递归处理剩余节点
        head.next = reverseKGroup(curr, k);
        
        return prev;
    }
}

7.4 判断链表是否为回文 (LeetCode 234)

/**
 * 判断链表是否为回文
 * 思路:找到中点,反转后半部分,然后比较
 */
public class PalindromeLinkedList {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) {
            return true;
        }
        
        // 找到中点
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        
        // 反转后半部分
        ListNode secondHalf = reverseList(slow.next);
        
        // 比较前半部分和后半部分
        ListNode firstHalf = head;
        while (secondHalf != null) {
            if (firstHalf.val != secondHalf.val) {
                return false;
            }
            firstHalf = firstHalf.next;
            secondHalf = secondHalf.next;
        }
        
        return true;
    }
    
    private ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        
        while (curr != null) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        return prev;
    }
}

8. 实际应用场景

8.1 浏览器历史记录

/**
 * 浏览器历史记录实现
 * 使用链表反转来实现前进后退功能
 */
public class BrowserHistory {
    private ListNode current;
    
    static class HistoryNode extends ListNode {
        String url;
        HistoryNode prev;
        HistoryNode next;
        
        HistoryNode(String url) {
            this.url = url;
        }
    }
    
    public BrowserHistory(String homepage) {
        current = new HistoryNode(homepage);
    }
    
    public void visit(String url) {
        // 清除当前节点之后的历史
        current.next = null;
        
        // 添加新页面
        HistoryNode newPage = new HistoryNode(url);
        current.next = newPage;
        newPage.prev = current;
        current = newPage;
    }
    
    public String back(int steps) {
        while (steps > 0 && current.prev != null) {
            current = current.prev;
            steps--;
        }
        return current.url;
    }
    
    public String forward(int steps) {
        while (steps > 0 && current.next != null) {
            current = current.next;
            steps--;
        }
        return current.url;
    }
}

8.2 音乐播放器

/**
 * 音乐播放器的播放列表
 * 支持反转播放顺序
 */
public class MusicPlayer {
    private ListNode playlist;
    private ListNode current;
    private boolean reversed = false;
    
    static class Song extends ListNode {
        String title;
        String artist;
        
        Song(String title, String artist) {
            this.title = title;
            this.artist = artist;
        }
        
        @Override
        public String toString() {
            return title + " - " + artist;
        }
    }
    
    public void addSong(String title, String artist) {
        Song newSong = new Song(title, artist);
        if (playlist == null) {
            playlist = newSong;
            current = newSong;
        } else {
            ListNode tail = playlist;
            while (tail.next != null) {
                tail = tail.next;
            }
            tail.next = newSong;
        }
    }
    
    public void reversePlaylist() {
        playlist = reverseList(playlist);
        reversed = !reversed;
        System.out.println("播放列表已" + (reversed ? "反转" : "恢复"));
    }
    
    public Song getCurrentSong() {
        return (Song) current;
    }
    
    public Song nextSong() {
        if (current != null && current.next != null) {
            current = current.next;
        }
        return (Song) current;
    }
    
    private ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        
        while (curr != null) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        return prev;
    }
}

8.3 文档编辑器的撤销功能

/**
 * 文档编辑器的撤销/重做功能
 * 使用链表反转实现操作历史管理
 */
public class DocumentEditor {
    private ListNode operationHistory;
    private ListNode currentState;
    
    static class Operation extends ListNode {
        String type;
        String content;
        int position;
        
        Operation(String type, String content, int position) {
            this.type = type;
            this.content = content;
            this.position = position;
        }
    }
    
    public void executeOperation(String type, String content, int position) {
        Operation op = new Operation(type, content, position);
        
        // 清除当前状态之后的历史
        if (currentState != null) {
            currentState.next = null;
        }
        
        // 添加新操作
        if (operationHistory == null) {
            operationHistory = op;
            currentState = op;
        } else {
            currentState.next = op;
            currentState = op;
        }
        
        System.out.println("执行操作: " + type + " - " + content);
    }
    
    public void undo() {
        if (currentState != null) {
            System.out.println("撤销操作: " + currentState.type);
            
            // 找到前一个操作
            if (currentState == operationHistory) {
                currentState = null;
            } else {
                ListNode prev = operationHistory;
                while (prev.next != currentState) {
                    prev = prev.next;
                }
                currentState = prev;
            }
        }
    }
    
    public void redo() {
        if (currentState == null && operationHistory != null) {
            currentState = operationHistory;
            System.out.println("重做操作: " + currentState.type);
        } else if (currentState != null && currentState.next != null) {
            currentState = currentState.next;
            System.out.println("重做操作: " + currentState.type);
        }
    }
}

9. 性能分析与优化

9.1 时间复杂度对比

方法时间复杂度空间复杂度优点缺点
迭代法O(n)O(1)空间效率高,性能稳定代码稍长
递归法O(n)O(n)代码简洁,思路清晰可能栈溢出

9.2 性能测试代码

/**
 * 性能测试类
 */
public class PerformanceTest {
    
    public static void comparePerformance() {
        int[] sizes = {100, 1000, 10000, 100000};
        
        System.out.println("链表大小\t迭代法(ns)\t递归法(ns)\t性能比");
        System.out.println("================================================");
        
        for (int size : sizes) {
            ListNode list1 = createLargeList(size);
            ListNode list2 = copyList(list1);
            
            // 测试迭代法
            long start1 = System.nanoTime();
            new Solution().reverseList(list1);
            long end1 = System.nanoTime();
            long iterativeTime = end1 - start1;
            
            // 测试递归法
            long start2 = System.nanoTime();
            try {
                new RecursiveSolution().reverseList(list2);
                long end2 = System.nanoTime();
                long recursiveTime = end2 - start2;
                
                double ratio = (double) recursiveTime / iterativeTime;
                System.out.printf("%d\t\t%d\t\t%d\t\t%.2f\n", 
                                size, iterativeTime, recursiveTime, ratio);
                
            } catch (StackOverflowError e) {
                System.out.printf("%d\t\t%d\t\t栈溢出\t\t-\n", size, iterativeTime);
            }
        }
    }
    
    private static ListNode createLargeList(int size) {
        if (size <= 0) return null;
        
        ListNode head = new ListNode(1);
        ListNode curr = head;
        
        for (int i = 2; i <= size; i++) {
            curr.next = new ListNode(i);
            curr = curr.next;
        }
        
        return head;
    }
    
    private static ListNode copyList(ListNode head) {
        if (head == null) return null;
        
        ListNode newHead = new ListNode(head.val);
        ListNode newCurr = newHead;
        ListNode curr = head.next;
        
        while (curr != null) {
            newCurr.next = new ListNode(curr.val);
            newCurr = newCurr.next;
            curr = curr.next;
        }
        
        return newHead;
    }
}

10. 总结与学习建议

10.1 核心要点总结

  1. 算法理解

    • 反转链表本质是改变指针方向
    • 关键是保存下一个节点的信息,避免链表断裂
    • 迭代法使用三指针,递归法利用调用栈
  2. 实现技巧

    • 边界条件:空链表和单节点链表
    • 迭代法:prevcurrnext 三指针配合
    • 递归法:明确递归终止条件和回溯处理
  3. 复杂度权衡

    • 迭代法:时间O(n),空间O(1),推荐使用
    • 递归法:时间O(n),空间O(n),适合理解递归思想

10.2 学习收获

通过学习反转链表问题,你应该掌握:

  • 链表操作的基本技巧
  • 指针操作的细节处理
  • 迭代与递归两种思维方式
  • 边界条件的重要性
  • 算法分析和优化方法

10.3 面试准备建议

  1. 熟练掌握基本实现

    • 能够快速写出迭代法的标准实现
    • 理解递归法的思想和实现
    • 处理好各种边界条件
  2. 扩展知识

    • 了解反转链表的变种题目
    • 掌握相关的链表操作技巧
    • 理解实际应用场景
  3. 调试能力

    • 能够快速定位和修复常见错误
    • 掌握链表问题的调试技巧
    • 具备代码review能力

10.4 进阶学习方向

  1. 更多链表题目

    • 合并两个有序链表
    • 环形链表检测
    • 链表排序
    • 复杂链表的复制
  2. 高级数据结构

    • 双向链表
    • 跳跃表
    • 链表与其他数据结构的结合
  3. 算法设计模式

    • 双指针技巧
    • 递归设计模式
    • 分治算法思想

反转链表是链表操作的经典问题,掌握它不仅能帮你解决相关题目,更重要的是培养处理链表问题的思维方式和编程技巧。持续练习和思考,你将在链表相关的算法问题上游刃有余!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈凯哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值