【链表】力扣206:反转链表(迭代/递归)
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例2:
输入:head = []
输出:[]
涉及到链表的操作,一定要在纸上把过程先画出来,再写程序。
从示例 1 的图片可以看出,实现反转实际上是变更指针 p.next 的指向。
参考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,实现一次局部反转
可以知道,prev 应初始化为 NULL。
-
局部反转完成之后,prev 和 curr 同时往前移动一个位置
-
循环上述过程,直至 curr 到达链表尾部。即 curr 为 NULL 时就不能继续循环了
此时 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 可以返回,进行第三步的组合。
2.3 组合
假设能够拿到小问题的解,就用小问题的解去构造大问题的解。
这里很明显,在 2 后面接上 1 就行了,但是怎么拿到 2 呢?
别忘了,原问题里,此时还有 1 指向 2 呢~也就是 node1.next = node2
然后把 2 指向 1:node2.next = node1
合起来就是:node1.next.next = node1
递推公式
- 明确递推公式的含义,在这里对于结点1来说,它只需要知道它之后的所有节点反转之后的结果就可以了,也就是说递推公式 reverseList 的含义是:把拿到的链表进行反转,然后返回新的头结点:
// 调用递推公式反转当前结点之后的所有节点,返回的结果是反转后的链表的头结点
ListNode newHead = reverseList(head.next);
}
-
接着要做的就是反转结点1,也就是将 head 指向的结点作为其下一个结点的下一个结点,即
head.next.next = head
。
-
最后,将 head 指向的结点的下一个结点置为 NULL,就完成了整个链表的反转。
head.next.next = head
head.next = None
return newHead
- 递归终止条件:在 head 指向的结点为 null 或 head 指向的结点的下一个结点为 null 时停止。因为在这两种情况下,反转后的结果就是它自己。这也就是上文提到的 base case。
if head == None or head.next == None:
return head
总图:
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 层。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理