206 反转链表:反转一个单链表。例如,输入:1->2->3->4->5->NULL
;输出:5->4->3->2->1->NULL
。
可以使用堆栈的后进先出特性来实现链表的反转。遍历链表中的节点,并存储在堆栈中;遍历完后,依次弹出堆栈中的节点,并将它与上一个节点链接起来。
时间复杂度:O(n)
,空间复杂度:O(n)
。
可以借助一前一后两个指针,在遍历链表的过程中,将两个前后两个节点的指向关系反转。
1 | 1 2 3 4 5 NULL |
时间复杂度:O(n)
,空间复杂度:O(1)
。
1 | struct ListNode* reverseList(struct ListNode* head){ |
对于链表中的每一个节点,我们 都将它插在新链表的头结点之前,对于链表 1->2->3->4->5->NULL
:
NULL
;NULL
的头结点之前,更新后的链表为 1->NULL
;1->NULL
的头结点之前,更新后的链表为 2->1->NULL
;4->3->2->1->NULL
的头结点之前,更新后的链表为 5->4->3->2->1->NULL
;时间复杂度:O(n)
,空间复杂度:O(1)
。
1 | struct ListNode* reverseList(struct ListNode* head){ |
双指针解法与头插法有什么区别?
双指针解法是反转前后两个节点的指向关系;头插法是初始化一个新的空链表,依次将待反转链表的节点,追加在新链表的最前面。
假设我们要反转链表 1->2->3->4->5->NULL
,可以先反转节点 1
后面 更短的链表,即 2->3->4->5->NULL
,这会得到 5->4->3->2->NULL
。最后,再将节点 1
链接到上面反转的链表的最后一个非空节点(即节点 2
)和 NULL
节点之间。
递归的终止条件就是链表无需反转了,即链表没有节点或只有一个节点。
时间复杂度:O(n)
,空间复杂度:O(n)
,递归深度可能达到 n
层。
1 | struct ListNode* reverseList(struct ListNode* head){ |
这段代码是用递归方式实现单链表的反转。让我们逐行解析代码的实现原理:
head
是否为空或者链表只有一个节点(即 head->next
为空)。如果是的话,直接返回头指针。reverseList
函数,传入 head->next
作为参数,目的是将 head->next
节点及之后的节点进行反转。cur
,它的尾结点是 head->next
。head
节点插入到反转后的链表的末尾,即 head->next->next = head
。这一步是将 head
节点的下一个节点(head->next
)指向 head
,实现反转。head
的下一个节点指向 NULL
,以确保链表的末尾指向 NULL
。cur
。这段代码的核心思想是通过递归来实现链表的反转。通过不断地调用 reverseList
函数,每次反转两个节点,最终实现整个链表的反转。