目录
题目来源:力扣(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;
}
二、解题思路
反转链表是一个经典的链表操作问题,核心思路是通过改变节点之间的指向关系来实现反转。我们可以使用三指针法来解决这个问题:
-
next1指针:指向当前节点的前一个节点(初始为NULL)
-
next2指针:指向当前节点(初始为头节点)
-
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)next1:记录已反转部分的头节点
(2)next2:当前正在处理的节点
(3)next3:保存原链表中的下一个节点,防止断链 -
核心操作:每次循环将当前节点的
next指向前一个节点,然后三个指针同步后移 -
循环终止条件:当
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)时间内完成反转操作,且不需要额外的存储空间。







1398

被折叠的 条评论
为什么被折叠?



