对于递归算法,我之前是真的半懂不懂,着实无奈,总抱着以后能懂的想法。现在学到的内容却发现递归是真的不懂。平时我们对于递归算法的要求比较低,因为觉得它的效率很低,但是它对于一些问题却能帮我们快速地解决,所以为了彻底搞懂它想了好久,一天两天了吧…希望这篇文章对你有帮助。
目录
1.递归的概念:
一个函数的递归调用是指一个函数在执行过程中出现了直接或间接调用函数本身的调用方式。如果直接调用函数本身,则称为直接递归;如果直接调用本身,则称为直接递归;如果调用另外一个函数,那个函数又调用该函数,则称为间接递归。通俗地来说,递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。
递归的三要素:
1.明确递归的终止条件
2.给出递归的终止时处理方法
2.提取重复地逻辑,缩小问题规模
2.递归的优缺点:
优点 | 缺点 |
---|---|
实现简单,可读性好 | 递归调用,占用空间大,递归太深,易发生栈溢出,可能存在重复计算 |
递归的强大之处在于它允许用户用有限的语句描述无限的对象(通俗地说,少代码代替多代码)。
3.递归的执行过程:
此图摘取于:递归过程
4.四种情况输出顺序去理解递归
4.1.数的阶乘(单层递归)
如:4!=423*1的执行过程如下:
4.2 分三种情况说明递归前后语句的执行情况(单层递归+前后语句)
递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序;
递归函数中,位于递归调用后的语句的执行顺序和各个调用函数的顺序相反。
第一种:for循环+前语句+递归:
如果按正常顺序执行代码,当n=3时,每个i都有一个f(2):
执行过程如下:这个输出就有点类似于二叉树的遍历,一直找,直到撞墙(结束条件),然后递归返回。
第二种:for循环+递归+后语句及程序输出结果:
第三种:for循环+前语句+递归+后语句,综上前面两种,不看结果,现在我们直接来手动输出一遍,其输出顺序如下:
public class Helloworld{
static int sum;
public static void main(String args[]){
f(3);
}
public static void f(int n){
if(n==1) {
System.out.println("end....");
return;
}
else {
for(int i=1;i<=n;i++){
System.out.println("i="+i+"start");
f(n-1);
System.out.println("i="+i+"****");
}
}
}
}
4.3.二叉树的递归遍历(二层递归):
先序遍历(DLR,父节点→左孩子节点→右孩子节点):DEBAC
中序和后序就不做模拟,我们可以自己试着去模拟一下。
void preOrder(Bitree bt){///先序遍历
if(bt==NULL) return;
printf("%c",bt->data);
preOrder(bt->lchild);
preOrder(bt->rchild);
}
中序遍历(LDR,左孩子节点→父节点→右孩子节点):
void InOrder(Bitree bt){///中序遍历
if(bt==NULL) return;
InOrder(bt->lchild);
printf("%c",bt->data);
InOrder(bt->rchild);
}
后序遍历(LRD,左孩子节点→右孩子节点→父节点):
void PostOrder(Bitree bt){///后序遍历
if(bt==NULL) return;
PostOrder(bt->lchild);
PostOrder(bt->rchild);
printf("%c",bt->data);
}
4.4. 找出可能的合的组合(多层递归)
小米oj:原题
以1,2 3一共三种为例子:
import java.util.*;
public class Main {
static int ans;
static int num[];
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
String line;
while (scan.hasNextLine()) {
ans = 0;
line = scan.nextLine().trim();
// please write your code here
String [] str = line.split(" ");
String [] str1 = str[0].split(",");
num = new int[str1.length];
int sum = Integer.parseInt(str[1]);
for(int i=0;i<str1.length;i++) num[i] = Integer.parseInt(str1[i]);
dfs(sum,num.length-1);
System.out.println(ans);
}
}
private static void dfs(int target,int cur){
///System.out.println("target:"+target+"cur:"+cur);
if(target<0||cur<0) return;
System.out.println("target:"+target+"cur:"+cur);
if(target==0) {
System.out.println("找到了");
ans++;
return;
}
dfs(target-num[cur],cur-1);
System.out.println("---------");
dfs(target-num[cur],cur);
System.out.println("---------");
dfs(target,cur-1);
}
}
总结:看了这个执行过程,我们可能会感慨,递归算法也不过如此。为什么?因为我们通过动笔去手动输出,彻底搞懂它的执行过程。如果我们对于该概念还不懂,我们可以用数学的方法去理解它,如阶乘递归:f(n) = n* f(fn-1)→f(n)=n* (n-1)* f(n-2)→…结束条件为n==0。函数是不断返回一个值,直到遇到结束条件。(不撞南墙不回头!!!)
5.参考资料
☞ 你为什么学不会递归?读完这篇文章轻松理解递归算法
☞ 《C语言程序设计》陈东旭和马迪芳版
☞ 递归算法的讲解