递归
关注于把大问题分解为小问题,不要纠结于具体过程,完全可以把递归函数看作一个魔法函数,而自己的唯一任务是简化原始问题,就假设递归方法已经是完成的,要考虑的是如何使用这个已经存在的递归方法;
例题
Leetcode206题
给你单链表的头节点 `head` ,请你反转链表,并返回反转后的链表。
该题除了使用如下的迭代方法解决:
迭代方法
/** * Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val;
}
* ListNode(int val, ListNode next) { this.val = val;
this.next = next;
}
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode next=cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
}
还可以使用递归的方式实现,代码更加简洁:
递归方法
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val;
}
* ListNode(int val, ListNode next) { this.val = val;
this.next = next;
}
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null){
return head;
//如果当前节点为空则直接返回head
}
ListNode newHead = reverseList(head.next);
head.next.next = newHead;
head.next = null;
return newHead;
}
}
递归图解
文章图片
【记录一下自己学习递归的过程(未完)】递归过程的调用以上图为例,小问题为对后面的节点进行反转,则reverseList返回的结果newHead就是后面的子链表反转后的结果,于是使反转得到的newHead的next指向当前的head,就实现了最简问题的反转,并使Head的next指向null。不要去纠结递归过程是怎样的,只要认为reverseList函数是一个已经存在的魔法函数并使用它的功能。
当然,使用递归的前提是它满足递归的几个条件:①可以大问题分成完全相同解决方法的小问题;②有终止条件③能调用自身。
数学归纳法 数学归纳法(Mathematical Induction, MI)是一种数学证明方法,通常被用于证明某个给定命题在整个(或者局部)自然数范围内成立。
最简单和常见的数学归纳法是证明当n等于任意一个自然数时某命题成立。证明分下面两步:
- 证明当n= 1时命题成立。
- 假设n=m时命题成立,那么可以推导出在n=m+1时命题也成立。(m代表任意自然数)
- 证明第一张骨牌会倒。
- 证明只要任意一张骨牌倒了,那么与其相邻的下一张骨牌也会倒。
那么便可以下结论:所有的骨牌都会倒下。
接下来考虑递归,递归工作时,一般会从大问题开始,一直执行到小问题,再层层返回参数。就像一个人打开一个门里面还有一个,一直开到最后一个门(达到递归终止条件),然后从最后一个门往回走,每出一个门就带回一个门的信息,直到回到初始门,完成每个门的信息处理。返回参数的过程就像数学归纳法从小到大的归纳过程。
不同之处是,数学归纳法我们可以很清楚地知道每一步是如何运作计算的,而递归的过程比较难以想象,而递归过程并不是代码编写者需要关注的事,把递归函数当作一个已经具备目标要求功能的魔法函数,用就可以了。
迭代的条件 递归函数必须要有终止条件:要使一个递归方法正常工作,必须使其对于越来越简单的示例不能有无限的归纳序列;递归归纳必须能得到一个可以用其他方法得到的基本情况,否则递归会无限循环下去。
递归三要素
- 确定递归函数的参数和返回值。
确定好哪些参数在传递过程中需要处理就在递归函数中加上这些参数,并且明确每次递归的返回值是什么,进而确定递归函数的返回类型。 - 确定终止条件。
写完递归算法,程序运行的时候经常会遇到栈溢出的错误,原因是没写终止条件或者终止条件错误。操作系统执行递归时也是要用栈的接哦古保存每一层递归的信息的,如果递归没有终止,那么操作系统的内存必然会溢出。 - 确定单层递归的逻辑。
确定每一层递归需要处理的信息。在这里会重复调用函数本身来实现递归过程。
推荐阅读
- LeetCode编程题解法汇总|力扣解法汇总41-缺失的第一个正数
- SCI|小学生发SCI论文,中学生发新英格兰,这不是后浪,这是海啸啊
- 大数据|基于图卷积堆叠的双向单向LSTM神经网络的地铁客流预测
- LeetCode编程题解法汇总|力扣解法汇总969- 煎饼排序
- LeetCode编程题解法汇总|力扣解法汇总917-仅仅反转字母
- leetcode|LeetCode --- 经典算法题之二分查找三回合
- Python|Python实现八大经典排序算法
- 数据结构|二叉树需要掌握的基本知识
- 算法初探|八大经典排序算法(java)