声明:我的链表OJ系列是针对无头单向不循环链表的题目
题目
题目来源:. - 力扣(LeetCode)
注意这里反转的是无头节点的单链表
思路1——就地反转
这个是基于前插操作改版而来的
struct ListNode {
int val;
struct ListNode *next;
};
struct ListNode* reverseList(struct ListNode* head) {
// 如果链表为空或只有一个节点,则不需要反转,直接返回
if (head == NULL || head->next == NULL)
return head;
struct ListNode* next = head; // 初始化next指针,用于遍历原链表
head = NULL; // 新的头节点初始化为NULL,因为反转后原来的头节点会变成尾节点
// 遍历原链表的每个节点
while (next != NULL) {
struct ListNode* cur = next; // 当前节点
next = next->next; // 将next指针指向下一个节点,以便在循环中继续遍历
// 将当前节点的next指针指向新的头节点,实现链表的反转
cur->next = head;
// 更新头节点为当前节点,因为当前节点现在是新的头节点
head = cur;
}
// 返回反转后的链表的头节点
return head;
}
反转链表的基本原理是通过改变链表节点的指向关系,使得原本指向下一个节点的指针现在指向前一个节点,从而实现链表的反转。
具体来说,反转链表的过程可以分为以下几个步骤:
-
初始化指针:通常我们会定义几个指针来帮助我们遍历和修改链表。在反转链表的算法中,至少需要两个指针,一个用于遍历原链表(通常叫做
next
或current
),另一个用于记录反转后的新链表的头节点(通常叫做head
或prev
)。 -
遍历链表:从链表的头节点开始,逐个遍历链表中的每个节点。在遍历的过程中,需要记录当前节点和前一个节点的信息。
-
修改指向关系:在遍历的过程中,将当前节点的
next
指针从指向下一个节点改为指向前一个节点。这样就实现了链表的反转。需要注意的是,在修改指向关系之前,需要保存下一个节点的信息,以便在遍历过程中能够继续向前移动。 -
更新头节点:随着遍历的进行,新的头节点会逐渐变为原链表的尾节点。因此,在每次修改指向关系后,都需要更新头节点的信息,使其指向当前节点。
-
返回新头节点:当遍历完整个链表后,原链表的头节点就变成了新链表的尾节点,而新链表的头节点则是遍历过程中最后更新的那个节点。因此,最后需要返回这个新的头节点。
这个过程中,关键在于理解链表节点的指向关系是如何改变的,以及如何通过遍历链表来逐步完成这个改变。通过不断练习和理解这个算法,可以加深对链表这种数据结构以及指针操作的理解。
思路2
struct ListNode* reverseList(struct ListNode* head)
{
if (head == NULL || head->next == NULL)//当链表为空或只有一个结点时,无需操作
return head;//直接返回
struct ListNode* n1 = NULL;//记录指针指向将要反转的结点反转后要指向的位置。
struct ListNode* n2 = head;//记录指针指向将要反转的结点。
struct ListNode* n3 = head->next;//记录指针指向将要反转的结点的下一个结点。
while (n2)//n2为NULL时,停止遍历
{
n2->next = n1;//反转结点指向
n1 = n2;//指针后移
n2 = n3;//指针后移
if (n3)//判断n3是否为NULL
n3 = n3->next;//指针后移
}
return n1;//返回n1指针指向的位置
}
这段代码实现的是链表的反转操作,其基本原理可以概括为以下几点:
- 初始化指针:
n1
:用于记录反转后当前节点的前一个节点,初始化为NULL
。n2
:用于遍历原链表,初始化为头节点head
。n3
:用于记录n2
的下一个节点,初始化为head->next
。
- 反转节点指向:
- 在每次循环中,先将
n2
节点的next
指针指向n1
,即实现当前节点的反转。
- 在每次循环中,先将
- 指针后移:
- 将
n1
移动到n2
的位置,因为n2
现在已经反转完成,它的下一个节点将是下一个要反转的节点。 - 将
n2
移动到n3
的位置,n3
是n2
原来的下一个节点。 - 如果
n3
不为NULL
,则将其移动到n3->next
,即继续向后遍历链表。
- 将
- 遍历结束条件:
- 当
n2
为NULL
时,表示已经遍历到链表的末尾,此时所有节点都已经反转完毕,循环结束。
- 当
- 返回新头节点:
- 由于
n1
在遍历过程中始终指向反转后链表的前一个节点,当遍历结束时,n1
将指向反转后链表的头节点。因此,函数返回n1
作为反转后链表的头节点。
- 由于
这个算法的关键在于理解如何通过三个指针来逐步改变链表中节点的指向关系,从而实现链表的反转。在遍历过程中,每次只改变一个节点的指向,并通过指针的移动来逐步处理链表中的其他节点,最终完成整个链表的反转。这种方法简单且高效,是链表操作中常见的技巧之一。