[OJ]数据结构:反转链表

目录

题目来源:力扣(LeetCode)

一、完整代码

二、解题思路

三、图解

1.第一次循环

2.第二次循环

3.第三次循环

4.第四次循环

5.第五次循环

6.循环结束

四、解析

五、总结

题目来源:力扣(LeetCode)

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

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

示例2:

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

示例3:

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

一、完整代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL || head->next == NULL)
    {
        return head;
    }
    struct ListNode* next1 = NULL;   // next1 指针
    struct ListNode* next2 = head;   // next2 指针
    struct ListNode* next3 = head->next; // next3 指针
    
    while(next2)
    {
        next2->next = next1;  // 反转当前节点的指向
        next1 = next2;        // next1指针后移
        next2 = next3;        // next2 指针后移
        if(next3 != NULL)
        {
            next3 = next3->next; // next3指针后移
        }
    }
    head = next1;
    return head;
}

二、解题思路

        反转链表是一个经典的链表操作问题,核心思路是通过改变节点之间的指向关系来实现反转。我们可以使用三指针法来解决这个问题:

  1. next1指针:指向当前节点的前一个节点(初始为NULL)

  2. next2指针:指向当前节点(初始为头节点)

  3. next3指针:指向当前节点的下一个节点

1.为什么需要三个指针?

        链表反转的核心矛盾在于:当我们改变一个节点的指向时,会失去对后续节点的引用。这就像在悬崖边走路,一步踏错就会坠入深渊。三指针法巧妙地解决了这个矛盾。

2.三个指针的意义

这其实是一个典型的状态转移问题:

  • 已处理(next1指向的部分)

  • 正在处理(next2指向的节点)

  • 待处理(next3指向的部分)

三、图解

让我们通过一个具体的例子来理解代码的执行过程:

next1 = NULL
next2 = 1 (head)
next3 = 2

1.第一次循环

1. next2->next = next1  // 1→NULL
2. next1 = next2        // next1指向1
3. next2 = next3        // next2指向2
4. next3 = next3->next  // next3指向3

当前状态:

2.第二次循环

1. next2->next = next1  // 2→1
2. next1 = next2        // next1指向2
3. next2 = next3        // next2指向3
4. next3 = next3->next  // next3指向4

当前状态:

3.第三次循环

1. next2->next = next1  // 3→2
2. next1 = next2        // next1指向3
3. next2 = next3        // next2指向4
4. next3 = next3->next  // next3指向5

当前状态:

4.第四次循环

1. next2->next = next1  // 4→3
2. next1 = next2        // next1指向4
3. next2 = next3        // next2指向5
4. next3 = next3->next  // next3指向NULL

当前状态:

5.第五次循环

1. next2->next = next1  // 5→4
2. next1 = next2        // next1指向5
3. next2 = next3        // next2指向NULL
4. 由于next3为NULL,不执行next3后移

当前状态:

6.循环结束

head = next1  // head指向5

最终结果:

四、解析

  1. 边界条件处理:首先检查链表为空或只有一个节点的情况,直接返回原链表

  2. 三指针协作
    (1)next1:记录已反转部分的头节点
    (2)next2:当前正在处理的节点
    (3)next3:保存原链表中的下一个节点,防止断链

  3. 核心操作:每次循环将当前节点的next指向前一个节点,然后三个指针同步后移

  4. 循环终止条件:当next2(当前节点)为NULL时停止,此时next1指向反转后的头节点

五、常见错误与解决方案

陷阱1:断链问题

// 错误示例:没有提前保存next指针
while(next2) 
{
    next2->next = next1;  // 断链!失去了对原next3的引用
    next1= next2;
    next2 = next2->next;  // 这里next2已经是NULL了
}

解决方案:提前保存下一个节点的引用,这正是next3的作用。

陷阱2:边界条件处理

空链表:如果不检查head == NULL,直接访问head->next会导致段错误。
单节点链表:如果不检查head->next == NULL,代码也能正确运行,但提前返回可以避免不必要的指针操作。

陷阱3:循环终止条件

// 错误示例:使用next3作为终止条件
while(next3) 
{
    // 当next3为NULL时,最后一个节点没有被处理
}

解决方案:使用next2(当前节点)作为终止条件,确保每个节点都被处理。

六、总结

        反转链表是链表操作中的基础问题,掌握三指针法是解决这类问题的关键。通过逐步改变节点指向,我们可以在O(n)时间内完成反转操作,且不需要额外的存储空间。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值