训练计划 III

标签: 递归 链表

难度: Easy

给定一个头节点为 head 的单链表用于记录一系列核心肌群训练编号,请将该系列训练编号 倒序 记录于链表并返回。

示例 1:

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

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

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

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

注意:本题与主站 206 题相同:https://leetcode-cn.com/problems/reverse-linked-list/

Submission

运行时间: 48 ms

内存: 15.5 MB

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None
        cur = head
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp
        return pre

Explain

该题解采用的是经典的迭代方法来反转单链表。首先,创建两个指针:'pre'(初始为None)和'cur'(初始为链表的头节点head)。然后,遍历整个链表。对于每个当前节点'cur',首先保存其下一个节点到临时变量'tmp'(这是为了在断开当前节点时不失去对链表其余部分的访问)。接着,将'cur'的下一个节点指向'pre',实现反向链接。然后,更新'pre'为当前节点'cur',并将'cur'移动到'tmp',即下一个待处理的节点。当'cur'为空时,即已经遍历完整个链表,此时'pre'指向新的头节点,即原链表的最后一个节点,完成反转。

时间复杂度: O(n)

空间复杂度: O(1)

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre = None  # 初始化前一个节点为None
        cur = head  # 初始化当前节点为头节点
        while cur:  # 当当前节点不为空时循环
            tmp = cur.next  # 保存当前节点的下一个节点
            cur.next = pre  # 将当前节点的next指针指向前一个节点
            pre = cur  # 移动前一个节点到当前节点
            cur = tmp  # 移动当前节点到下一个节点
        return pre  # 当循环结束时,pre将是新的头节点,返回pre

Explore

在反转链表的过程中,我们需要将当前节点的`next`指针指向前一个节点。如果直接修改`cur.next`而不先保存其原始值,我们将失去对链表剩余部分的访问。因此,使用临时变量`tmp`来保存`cur.next`的值是必要的,这样在断开`cur`与`cur.next`的连接后,我们仍然可以通过`tmp`访问链表的下一个节点,继续迭代过程。

当链表长度为0(即链表为空)时,`head`为`None`。算法中`cur`初始化为`head`,因此`cur`也为`None`,不进入循环,直接返回`pre`(也是`None`),这是正确的。当链表长度为1时,只有一个节点,反转后的链表与原链表相同。在这种情况下,循环只执行一次,`cur.next`被设置为`pre`(即`None`),然后返回新的头节点`pre`,即为原头节点。因此,这个算法自然地处理了长度为0或1的边界情况,不需要特殊处理。

算法在处理非常长的链表时主要受时间复杂度的影响,时间复杂度为O(n),其中n是链表中的节点数。由于该过程是线性遍历,且只使用了几个额外的指针变量(即`pre`、`cur`和`tmp`),空间复杂度为O(1)。因此,算法不会因为链表长度的增加而导致内存溢出,它在空间上是高效的。性能主要受到单次循环中操作的速度和系统的内存管理效率影响,但通常不会有性能问题。

`pre`和`cur`分别表示在迭代中的前一个节点和当前节点。过程开始时,`pre`初始化为`None`(代表新链表的尾部),`cur`初始化为链表的头节点。在每次迭代中,我们首先使用`tmp`保存`cur.next`(即当前节点的下一个节点),然后将`cur.next`设置为`pre`,这样当前节点就指向了前一个节点,实现了部分反转。接着,`pre`更新为当前的`cur`,`cur`更新为`tmp`(即之前的`cur.next`)。这样,`pre`和`cur`逐步向前移动,直至`cur`为`None`,此时`pre`指向新的头节点。通过这种方式,链表被逐步反转。