ch2_4 单链表的反转(迭代与递归的区别)

https://leetcode-cn.com/problems/reverse-linked-list/
链表反转 206

1单链表的反转

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
在这里插入图片描述

1.迭代与递归的区别

1.1 迭代 iteration

迭代通常是指重复执行某个任务的控制结构,
在迭代中, 在满足条件下 重复执行某段代码, 直到条件不满足;
通常有循环的方式实现;

  1. for 循环;

  2. while 循环

  3. 嵌套循环;

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/;

在这里插入图片描述

  1. 由于此处, 只是反转节点, 故没有设置虚拟头节点。
    初始化两个节点
    一个表示当前节点, 初始值为 head, 一个表示前置节点, 设置为None, (这也代表原始链表尾节点的后继节点。)

  2. 使用临时节点 temp 用于保存当前节点的后继节点: temp = cur. next;
    更新当前节点的下一个节点为 前置节点: cur.next = pre;
    更新前置节点为 当前节点 pre = cur
    更新当前节点为temp, 即开始时当前节点的后继节点, cur= temp

  3. 循环, 直到当前节点 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) 递归函数:

  1. 终止条件:当 cur 为空,则返回尾节点 pre (即反转链表的头节点);
  2. 递归后继节点,记录返回值(即反转链表的头节点)为 res ;
  3. 修改当前节点 cur 引用指向前驱节点 pre ;
  4. 返回反转链表的头节点 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值