206. 反转链表 - 题解与详细分析

题目描述

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

示例

示例 1:

复制代码
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

复制代码
输入:head = [1,2]
输出:[2,1]

解题思路

反转链表是链表操作中的经典问题,需要改变每个节点的指针方向。核心思想是遍历原链表,将每个节点的 next 指针指向前一个节点。

关键点分析

  1. 指针方向改变 :每个节点的 next 指针需要反向

  2. 头尾节点变化:原来的头节点变成尾节点,原来的尾节点变成新的头节点

  3. 边界情况:空链表、单节点链表等情况

代码实现

复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* newhead = NULL;  // 新链表的头节点
    struct ListNode* cur = head;      // 当前遍历的节点
    
    while(cur)
    {
        if(newhead == NULL)
        {
            // 处理第一个节点:将其作为新链表的头节点
            struct ListNode* temp = cur->next;  // 保存下一个节点
            newhead = cur;                      // 当前节点成为新头节点
            newhead->next = NULL;               // 新头节点的next置为NULL
            cur = temp;                         // 移动到下一个节点
        }
        else
        {
            // 处理后续节点:头插法
            struct ListNode* temp = cur->next;  // 保存下一个节点
            cur->next = newhead;                // 当前节点指向新头节点
            newhead = cur;                      // 更新新头节点为当前节点
            cur = temp;                         // 移动到下一个节点
        }
    }
    return newhead;
}

代码详解

核心思想:头插法

使用头插法构建反转后的链表:

  • 遍历原链表,逐个取出节点

  • 将取出的节点插入到新链表的头部

  • 最终得到的就是反转后的链表

变量说明

  • newhead :新链表的头节点,初始为 NULL

  • cur:当前正在处理的节点,从原链表的头节点开始遍历

  • temp:临时变量,用于保存下一个节点的地址

执行流程

第一步:处理第一个节点

复制代码
if(newhead == NULL)
{
    struct ListNode* temp = cur->next;
    newhead = cur;
    newhead->next = NULL;
    cur = temp;
}
  • 保存下一个节点:temp = cur->next

  • 当前节点成为新头节点:newhead = cur

  • 新头节点的 next 置为 NULL(因为是反转后的尾节点)

  • 移动到下一个节点:cur = temp

第二步:处理后续节点

复制代码
else
{
    struct ListNode* temp = cur->next;
    cur->next = newhead;
    newhead = cur;
    cur = temp;
}
  • 保存下一个节点:temp = cur->next

  • 当前节点指向新头节点:cur->next = newhead(关键的反转操作)

  • 更新新头节点:newhead = cur

  • 移动到下一个节点:cur = temp

可视化过程

[1,2,3,4,5] 为例:

复制代码
初始状态:
head -> 1 -> 2 -> 3 -> 4 -> 5 -> NULL
newhead = NULL

第1次循环:
取出节点1:newhead -> 1 -> NULL
剩余链表:2 -> 3 -> 4 -> 5 -> NULL

第2次循环:
取出节点2:newhead -> 2 -> 1 -> NULL
剩余链表:3 -> 4 -> 5 -> NULL

第3次循环:
取出节点3:newhead -> 3 -> 2 -> 1 -> NULL
剩余链表:4 -> 5 -> NULL

第4次循环:
取出节点4:newhead -> 4 -> 3 -> 2 -> 1 -> NULL
剩余链表:5 -> NULL

第5次循环:
取出节点5:newhead -> 5 -> 4 -> 3 -> 2 -> 1 -> NULL
剩余链表:NULL

优化后的代码

实际上,可以简化代码,去掉 if-else 判断:

复制代码
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* newhead = NULL;
    struct ListNode* cur = head;
    
    while(cur) {
        struct ListNode* temp = cur->next;  // 保存下一个节点
        cur->next = newhead;                // 反转指针
        newhead = cur;                      // 更新新头节点
        cur = temp;                         // 移动到下一个节点
    }
    
    return newhead;
}

这个简化版本逻辑更清晰,因为 newhead 初始为 NULL,第一次循环时 cur->next = newhead 就相当于 cur->next = NULL,与原来 if 分支的效果相同。

复杂度分析

  • 时间复杂度:O(n),需要遍历整个链表一次

  • 空间复杂度:O(1),只使用了常数级别的额外空间

其他解法

方法二:递归法

复制代码
struct ListNode* reverseList(struct ListNode* head) {
    // 递归终止条件
    if (head == NULL || head->next == NULL) {
        return head;
    }
    
    // 递归反转后续链表
    struct ListNode* newHead = reverseList(head->next);
    
    // 反转当前节点
    head->next->next = head;
    head->next = NULL;
    
    return newHead;
}

递归思路

  1. 递归到链表末尾,返回新的头节点

  2. 在回溯过程中,逐个反转指针方向

方法三:双指针法

复制代码
struct ListNode* reverseList(struct ListNode* head) {
    struct ListNode* prev = NULL;
    struct ListNode* cur = head;
    
    while (cur) {
        struct ListNode* temp = cur->next;
        cur->next = prev;
        prev = cur;
        cur = temp;
    }
    
    return prev;
}

这实际上就是我们优化后的迭代方法,只是变量命名不同。

总结

反转链表是链表操作的基础题,掌握迭代法和递归法都很重要:

  1. 迭代法:思路清晰,性能好,推荐掌握

  2. 递归法:代码简洁,但需要理解递归的调用栈

  3. 关键操作:保存下一个节点 → 反转指针 → 更新指针位置

这道题的解法体现了链表操作的核心技巧:通过临时变量保存节点地址,安全地改变指针指向

相关推荐
小弥儿1 分钟前
GitHub今日热榜 | 2026-09-01:迷你小模型登场
学习·microsoft·开源·github
奈斯先生Vector2 分钟前
本地图片识别怎么接入多模态 AI?用 Python API 理解 GPT-4o Vision 的真实工作流
开发语言·人工智能·windows·python·网络协议·http·aigc
OPEN-F6 分钟前
C++17新特性精讲:结构化绑定、if constexpr与实用类型
开发语言·c++
CoderYanger7 分钟前
A.每日一题:3345. 最小可整除数位乘积 I
java·数据结构·程序人生·算法·leetcode·面试·学习方法
带多刺的玫瑰8 分钟前
Leecode#16刷题之最接近的三数之和
数据结构·算法
OPEN-F9 分钟前
C++并发编程:线程与互斥锁
开发语言·c++
俊昭喜喜里11 分钟前
C#中的decimal
开发语言·c#
橘子汽水16815 分钟前
Leetcode15:三数之和
java·数据结构·算法
QUOR18 分钟前
Zorv AI 内置浏览器技术架构与开发指南(新版)
架构·github
catchadmin22 分钟前
免费可商用 PHP 管理后台 CatchAdmin V5.4.0 发布,新增短信服务能力
开发语言·php