解析递归的本质:栈(多图预警!)

关于递归和栈。

递归调用的本质就是栈的先进后出,递归可分为三个阶段:

  1. 前进段:依次执行递归语句之前的代码,保存当前函数的现场(参数、局部变量等),并压入栈中。
  2. 临界值:终止递归的前进段,通过 return 语句开始进入返回段。
  3. 返回段:对于栈顶的函数,基于在前进段保存的函数的现场,依次执行递归语句之后的代码,执行完后弹出栈(执行 return 语句即出栈)。

一、实战解析:两两交换链表中的结点

仿照 LeetCode 第 24 题 两两交换链表中的结点,(如果不熟悉这道题,建议先去 LeetCode 把这道题做了)有以下问题:

给定一个链表,两两交换其中相邻的结点,并返回交换后的链表。你不能只是单纯的改变结点内部的值,而是需要实际的进行结点交换。示例:

给定 a->b->c->d, 你应该返回 b->a->d->c.

这里采用递归的方式解题,代码如下:

public static class ListNode{
    char val;
    ListNode next;
    ListNode(char ch){
      val = ch;
      next = null;
    }
}

public ListNode swapPairs(ListNode head) {
    if (head == null || head.next == null){ // No.1
      // 判断临界条件
      return head; // N0.2
    } else {
      ListNode nextNode = head.next; // No.3
      head.next = swapPairs(nextNode.next); // No.4
      nextNode.next = head; // No.5
      return nextNode; // No.6
    }
}

对于上面的递归算法,按照「前进段 -> 临界条件 -> 返回段」的流程,有以下解析:

1. 前进段

注: 这里在进行说明的时候,用双引号包裹字符表示该字符的结点(ListNode),如用 “a” 表示这是结点 a。

当函数 swapPairs() 开始执行的时候,先将当前函数入栈,标记为函数 ①。从第 No.1 行代码开始依次执行程序,此时有结点 head = “a”,不满足临界条件,然后执行代码 ListNode nextNode = head.next; ,执行后的链表结点情况如下:

前进段-1

函数①-入栈
执行到第 No.4 行的递归语句 swapPairs(nextNode.next) 的时候,保存当前函数的现场,将结点 nextNode.next = "c" 作为参数传递到下一个将要入栈的函数中。
注意, 第 No.4 行左边的代码 head.next = 位于递归语句之后,在返回段的时候才会执行。)

然后通过递归语句 swapPairs(nextNode.next) 进入结点 head = “c” 的函数中,先将当前函数入栈,标记为函数 ②。依次执行代码 ListNode nextNode = head.next; 后的链表结点情况如下:

前进段-2

函数②-入栈

执行到第 No.4 行的递归语句时,保存当前函数的现场,将结点 nextNode.next = null 作为参数传入到下一个将要入栈的函数中。

然后通过递归语句 swapPairs(nextNode.next) 进入结点 head = null 的函数中,先将当前函数入栈,标记为函数 ③:

函数③-入栈

依次执行代码,在执行到第 No.1 行的判断语句时有结点 head = null,满足临界条件,此时通过第 No.2 行的 return 语句将栈顶的函数 ③ 出栈。将结点 head = null 作为返回值传递给新的栈顶函数,进入返回段

2. 返回段

将满足临界条件的函数 ③ 出栈后,进入到返回段的第一个函数,也就是栈顶的函数 ②。此时,基于之前入栈时保存的函数现场,依次执行执行递归语句之后的代码:

	// 这里的 swapPairs(nextNode.next) 的值即为上一个栈顶函数的返回值
	head.next = swapPairs(nextNode.next); // No.4
	nextNode.next = head; // No.5
	return nextNode; // No.6

执行完后的链表结点情况如下:

返回段-1

函数③-出栈

执行到第 No.6 行的 return 语句时,当前函数执行完毕,然后出栈,将结点 nextNode 作为返回值传递给新的栈顶函数。

对于当前栈顶的函数 ①,基于之前入栈时保存的函数现场,依次执行执行递归语句之后的代码:

	// 这里的 swapPairs(nextNode.next) 的值即为上一个栈顶函数的返回值
	head.next = swapPairs(nextNode.next); // No.4
	nextNode.next = head; // No.5
	return nextNode; // No.6

执行完后的链表结点情况如下:

函数②-出栈

返回段-2

执行到第 No.6 行的 return 语句时,当前函数执行完毕出栈。此时递归调用栈的函数全部执行完毕,所以将结点 nextNode 作为最终结果返回:

最终结果


(完)如有问题,欢迎交流~

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值