https://leetcode-cn.com/problems/reverse-linked-list/
链表反转 206
1单链表的反转
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
1.迭代与递归的区别
1.1 迭代 iteration
迭代通常是指重复执行某个任务的控制结构,
在迭代中, 在满足条件下 重复执行某段代码, 直到条件不满足;
通常有循环的方式实现;
-
for 循环;
-
while 循环
-
嵌套循环;
def nested_for_loop(n: int) -> str:
"""双层 for 循环"""
res = ""
# 循环 i = 1, 2, ..., n-1, n
for i in range(1, n + 1):
# 循环 j = 1, 2, ..., n-1, n
for j in range(1, n + 1):
res += f"({i}, {j}), "
return res
1.2 递归
则是一种自己调用 自己的函数 方式;
主要包含两个阶段:
递归(recursion)是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。
递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。
归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。
而从实现的角度看,递归代码主要包含三个要素。
终止条件:用于决定什么时候由“递”转“归”。
递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。
返回结果:对应“归”,将当前递归层级的结果返回至上一层。
观察以下代码,我们只需调用函数 recur(n) ,就可以完成 1+2+3+。。。+N
的计算:
def recur(n: int) -> int:
"""递归"""
# 终止条件
if n == 1:
return 1
# 递:递归调用
res = recur(n - 1)
# 归:返回结果
return n + res
虽然从计算角度看,迭代与递归可以得到相同的结果,但它们代表了两种完全不同的思考和解决问题的范式。
- 迭代:“自下而上”地解决问题。从最基础的步骤开始,然后不断重复或累加这些步骤,直到任务完成。
- 递归:“自上而下”地解决问题。将原问题分解为更小的子问题,这些子问题和原问题具有相同的形式。接下来将子问题继续分解为更小的子问题,直到基本情况时停止(基本情况的解是已知的)。
以上述求和函数为例,设问题 f(n)=1+2+⋯+n 。
- 迭代:在循环中模拟求和过程,从 1 遍历到 n ,每轮执行求和操作,即可求得 f(n) 。
- 递归:将问题分解为子问题 f(n)=n+f(n−1) ,不断(递归地)分解下去,直至基本情况 f(1)=1 时终止。
2. 迭代法实现
代码参考
code 参考
2.1 关键点
图解
https://leetcode.cn/problems/reverse-linked-list/;
-
由于此处, 只是反转节点, 故没有设置虚拟头节点。
初始化两个节点
一个表示当前节点, 初始值为 head, 一个表示前置节点, 设置为None, (这也代表原始链表尾节点的后继节点。) -
使用临时节点 temp 用于保存当前节点的后继节点: temp = cur. next;
更新当前节点的下一个节点为 前置节点: cur.next = pre;
更新前置节点为 当前节点 pre = cur
更新当前节点为temp, 即开始时当前节点的后继节点, cur= temp -
循环, 直到当前节点 cur 为空:
则此时返回前置节点pre 作为新的头节点.
2.2 coding
class LinkNode:
def __init__(self, x):
self.val = x
self.next = None
# 迭代方法
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 初始化两个节点, 一个表示当前节点cur, 初始值为 head; 一个表示前置节点, 初值为None;
cur = head
pre = None # 此时也表示原始链表的尾节点的后继
while (cur != None): # 只要当前节点不为空; 则对当前节点的后继节点开始反转;
temp = cur.next # 保存当前节点的后继节点;
cur.next = pre # 将当前节点的后继节点进行更新为前置节点, 实现当前节点的前后节点发生变化, 实现一种反转的效果;
pre = cur # 前置节点更新为 当前节点
cur = temp # 当前节点更新temp, 即之前当前节点的后继节点;
return pre # 当前节点为None 空时 ,返回前置节点作为头结点;
3. 递归法
代码参考
code 参考
考虑使用递归法遍历链表,当越过尾节点后终止递归,在回溯时修改各节点的 next 引用指向。
recur(cur, pre) 递归函数:
- 终止条件:当 cur 为空,则返回尾节点 pre (即反转链表的头节点);
- 递归后继节点,记录返回值(即反转链表的头节点)为 res ;
- 修改当前节点 cur 引用指向前驱节点 pre ;
- 返回反转链表的头节点 res ;
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
def recur(cur, pre):
if not cur: return pre # 递归的终止条件, 当前节点是None 时, 返回前置节点;
# 递归后继节点, 传入的参数 当前节点 更新当前节点的后继节点, 前置节点 更新为当前节点
# 保存返回值,即反转链表的头节点, 即res;
res = recur(cur.next, cur) # 递 的过程
cur.next = pre # 满足条件后, 归的过程, 将当前节点的后继节点更新为 前置节点;
return res
return recur(head, None)
reverseList(head) 函数:
调用并返回 recur(head, null) 。传入 null 是因为反转链表后, head 节点指向 null ;
# 递归法
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
def recur(cur, pre):
if not cur: return pre # 终止条件
res = recur(cur.next, cur) # 递归后继节点
cur.next = pre # 修改节点引用指向
return res # 返回反转链表的头节点
return recur(head, None) # 调用递归并返回
ref
https://www.hello-algo.com/chapter_computational_complexity/iteration_and_recursion/#3