【链表】力扣206:反转链表(迭代/递归)

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

示例1:

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

示例2:

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

涉及到链表的操作,一定要在纸上把过程先画出来,再写程序。

从示例 1 的图片可以看出,实现反转实际上是变更指针 p.next 的指向

参考1:https://leetcode.cn/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-shuang-zhi-zhen-di-gui-yao-mo-/

参考2(递归和迭代都讲得特别清楚):https://segmentfault.com/a/1190000037518253

参考3(对递归函数的讲解很好):
链接:https://leetcode.cn/problems/reverse-linked-list/solution/yi-bu-yi-bu-jiao-ni-ru-he-yong-di-gui-si-67c3/

1 迭代(双指针)

  • 定义两个变量 prev 和 curr。curr 在前,prev 在后

  • 每次让 curr 的 next指针 指向 prev,实现一次局部反转
    image

    可以知道,prev 应初始化为 NULL。

  • 局部反转完成之后,prev 和 curr 同时往前移动一个位置
    image

  • 循环上述过程,直至 curr 到达链表尾部。即 curr 为 NULL 时就不能继续循环了
    image
    此时 prev 就是新链表的 head,因此最终 return 的是 prev。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        curr, prev = head, None # 实际上空节点表达一般是NULL
        while curr != None:
            curr.next = prev # 反转箭头
            prev, cur = curr, curr.next # prev 和 curr 均往前移动一个位置
        return prev

时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。

空间复杂度:O(1)。

2 递归

递归三步骤: Base case + 拆解 + 组合

2.1 base case

只有一个 node,或者没有 node:

if node == None or node.next == None:
            return node

其实,只剩一个 node 严格来讲并不是 base case,而是 corner case。因为它本可以再 break down 到 node == null 的,但因为后面有对 node.next 的 dereference 操作,所以不能省略。

2.2 拆解

把大问题分解成小一点点的问题,直到 base case 可以返回,进行第三步的组合。

image

2.3 组合

假设能够拿到小问题的解,就用小问题的解去构造大问题的解。

image

这里很明显,在 2 后面接上 1 就行了,但是怎么拿到 2 呢?

别忘了,原问题里,此时还有 1 指向 2 呢~也就是 node1.next = node2

然后把 2 指向 1:node2.next = node1

合起来就是:node1.next.next = node1

递推公式

  1. 明确递推公式的含义,在这里对于结点1来说,它只需要知道它之后的所有节点反转之后的结果就可以了,也就是说递推公式 reverseList 的含义是:把拿到的链表进行反转,然后返回新的头结点:
    image
// 调用递推公式反转当前结点之后的所有节点,返回的结果是反转后的链表的头结点
    ListNode newHead = reverseList(head.next);
}
  1. 接着要做的就是反转结点1,也就是将 head 指向的结点作为其下一个结点的下一个结点,即 head.next.next = head
    image

  2. 最后,将 head 指向的结点的下一个结点置为 NULL,就完成了整个链表的反转。
    image

    head.next.next = head
    head.next = None
    return newHead
  1. 递归终止条件:在 head 指向的结点为 null 或 head 指向的结点的下一个结点为 null 时停止。因为在这两种情况下,反转后的结果就是它自己。这也就是上文提到的 base case。
    if head == None or head.next == None:
        return head

总图:
image

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head == None or head.next == None:
            return head
        newHead = self.reverseList(head.next) # 递归:self.reversetList 是反转链表,newHead 是原链表的尾结点,也是新链表的第一个结点
        head.next.next = head # 当前结点的原下一个结点的 next 指针转向,指向当前节点:head 在递归中就是当前结点,此时 head.next 没有变,是原链表中 head 的下一个结点
        head.next = None # 让当前结点的 next 指针指向 NULL,实现局部反转。为了简洁,这两句在 pyhton 中可以合并为一行
        return newHead

时间复杂度:O(n),其中 n 是链表的长度。需要对链表的每个节点进行反转操作。

空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。

posted @   Vonos  阅读(169)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示