what:在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法
when:发现问题可以分解为同类子问题且采用同样的方式去解决
how:找到递归出口和递归体
步骤:通过分析题目是否可以分解为若干重复子问题,判断是否可以采用递归算法进行解决。确定采用递归算法之后,开始找递归出口和递归体,这是递归算法的核心部分,下面通过两个题目讲一下我的解题步骤。
P1:leetcode226 翻转二叉树 简单题
解答:题目很简单,翻转二叉树,然后查看输入输出,其实就是将每个节点的左右子树进行了交换。这个时候子问题已经出现了,交换左右子树,按题目说法就是翻转左右子树,可以采用递归来做。
接下来需要找到递归出口和递归体,一般情况下先找递归体,因为确定递归的时候大概就已经确定了递归体,然后就是考虑边界问题,确定递归出口。
回到题目,根据题目的分析,可以确定递归体即为交换翻转当前节点的左右子树,当遍历至根节点无节点可翻转时即退出递归,即找到递归出口当前节点为null。
接下来代码实现:
public class Solution{ /** 226. 翻转二叉树 简单题 * */ public TreeNode invertTree(TreeNode root) { this.invert(root); return root; } private void invert(TreeNode root){ if (null == root) { return; } TreeNode left = root.left; TreeNode right = root.right; root.right = invertTree(left); root.left = invertTree(right); } } class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() { } TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
P2:leetcode24,给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。中等题
解答:这个题目也可以采用递归来做(但是综合考虑的话,递归其实不是最好的方法,这里只是用来举例说明一下递归该怎么去实现)。同样的找递归体递归出口,递归出口很简单,链表遍历完,递归就结束了,但是需要考虑,子问题是需要两两交换,所以递归应该满足两个条件,当前节点不能为空以及当前节点的下个节点不能为空。而递归体,观察我们需要完成的问题,将相邻两个节点交换,那么我们一直重复做的事情就是在交换两个相邻节点以及将交换后的节点正确链接到下个节点,递归体也找到了,接下来实现。(递归体实现的时候对链表的考察,这里注意好赋值顺序一般问题不大,最好的就是画图,然后就是递归体之间的链接需要考虑到,如果把递归体当做一个整体的话,当前块要链接到下一块)
接下来代码实现:
/** * 24. 两两交换链表中的节点 */ public ListNode swapPairs(ListNode head) { //1.出口 if (null == head || null == head.next) { return head; } //2.找到递归体 //两两交换 //下次递归头结点 ListNode nextHead = head.next.next; //相邻节点交换 ListNode temp = head.next; head.next = nextHead; temp.next = head; //重置头结点 head = temp; //设置当前组指向下一个头结点 head.next.next = swapPairs(nextHead); return head; }
最后:递归应该算是最简单的算法,一般入门就是递归,容易想到,实现简单。但是并不是能够采用递归算法,递归算法就是最优的选择,在选择算法时候,需要根据应用场景进行选择,因为递归需要递归栈保存现场信息,带来额外的空间消耗。工作中最常使用递归的应该就是构造部门树了,数据量小,时间空间都不会占用太多,递归实现很方便。
下集预告:贪心算法
(可扫描我的微信公众号二维码关注我的公众号,更快获取更新哦)