题目:反转一个单链表。
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL || head->next == NULL)
return head;
struct ListNode* n1 = NULL, *n2 = head, *n3 = head->next;
while(n2)
{
// 反转
n2->next = n1;
// 迭代
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
return n1;
}
"反转单链表"功能,将形如 1->2->3->4->5->NULL 的链表就地修改为 5->4->3->2->1->NULL。它采用的是三指针法,核心思想是在遍历链表的过程中,逐个打断原本的节点指向并让其"掉头"。
1. 边界条件拦截
if(head == NULL || head->next == NULL)
return head;
如果传入的链表是空的,或者里面只有一个节点,那么反转后的结果就是它本身,直接返回 head 即可,无需执行后续逻辑。
2. 初始化三个核心指针
代码开头定义了三个相互配合的指针:
n1(前驱指针) :初始化为NULL。原链表的第一个节点(头节点)在反转后会变成最后一个节点(尾节点),它的next必须指向NULL,这就是n1初始为NULL的原因。n2(当前指针) :初始化为head。它代表当前正在被处理(准备掉头)的节点。n3(后继指针) :初始化为head->next。这正是你上一个问题中遇到的逻辑 :因为在反转时会修改n2->next的值,如果不提前用n3记住下一个节点的位置,修改指针后就会和后面的链表彻底失联。
3. 循环反转与迭代
进入 while(n2) 循环,只要当前节点还有效,就持续执行以下动作:
- 执行反转 (
n2->next = n1;) :这是最关键的一步,直接把当前节点n2的指针方向反转,让它指向上一个节点n1。 - 指针整体平移:完成当前节点的反转后,三个指针像履带一样集体向后滑动一步,准备处理下一个节点。
n1 = n2;:n1走到当前节点的位置。n2 = n3;:n2走到下一个节点的位置。if(n3) n3 = n3->next;:n3继续往前探路。这里的if判断是为了安全防御------当n2已经走到最后一个节点时,n3已经是NULL了,如果强行读取NULL->next会引发段错误(内存访问违规)。
4. 循环结束与返回
当 n2 变成 NULL 时,说明所有节点都已经处理完毕,跳出 while 循环。
此时,n2 掉出了链表边界,而 n1恰好停留在原链表的最后一个节点上 。这个节点也就是反转后的"新头节点",因此最终返回 n1 即可将新的链表交接出去。
struct ListNode* reverseList(struct ListNode* head){
if(head == NULL || head->next == NULL)
return head;
struct ListNode* n1 = NULL, *n2 = head, *n3 = head->next;
while(n2)
{
// 反转
n2->next = n1;
// 迭代
n1 = n2;
n2 = n3;
if(n3)
n3 = n3->next;
}
return n1;
}