本文内容,参考自《大话数据结构》(程杰著) ,一部分自己修改,如:把C语言换成了Java语言。写作目的,意在加强记忆。
本文写作工具,使用 Typora。
栈的应用——递归
栈有一个很重要的应用:在程序设计语言中实现了递归。
我们先来看一个经典的递归例子:斐波那契数例。
如果兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子。假设所有兔子都不死,那么一年后可以繁殖多少对兔子呢?
我们拿新出生的一对兔子分析一下:第一个月小兔子没有繁殖能力,所以还是一对;两个月后,生下一对小兔子,共有两对;三个月后,老兔子又生下一对,没有小兔子还没有繁殖能力,所以一共三对。。。依次类推(表格有限,不列举太多)
所经过月数 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
兔子对数 | 1 | 1 | 2 | 3 | 5 |
表中数学1,1,2,3,5,8,13 。。。构成了一个序列。这个数列有个十分明显的特点,就是:前面相邻两项之和,构成了后一项。可以得到以下结论:
F(n) = 0 ,当n=0
F(n) = 1, 当n=1
F(n) = F(n-1) + F(n-2) , 当n>1
如果我们要实现这样的数列用常规的迭代的办法,假设我们需要打印出前 40 位的斐波那契数列,代码如下:
public class Fibonacci {
public static void main(String[] args) {
int[] a = new int[40];
a[0] = 0;
a[1] = 1;
System.out.println(a[0]);
System.out.println(a[1]);
for(int i=2; i<40; i++) {
a[i] = a[i-1] + a[i-2];
System.out.println(a[i]);
}
}
}
复制代码
代码很简单,不用做什么解释。但其实我们的代码,如果用递归来实现,还可以更简单。
public void recursion() {
for(int i=0; i<40; i++) {
System.out.println(fibonacci(i));
}
}
public int fibonacci(int i) {
if(i<2) {
return i==0 ? 0 : 1;
}
return fibonacci(i-1) + fibonacci(i-2);
}
复制代码
如上面的代码,fibonacci 函数不断地调用自己来完成计算。我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。
当然,递归程序最怕就是陷入永不结束的无穷递归中,所以,每个递归定义必须至少有一个条件,满足时递归不再进行,即不再引用自身而是返回值退出。比如刚刚的例子,总有一次递归会使得 i<2 ,这样就可以执行 return i 的语句而不用继续递归。
迭代和递归的区别:迭代使用的是循环结构,递归使用的是选择结构。递归能使程序的结构更清晰、更简洁、更容易让人理解。但大量的递归调用会建立函数的副本,会耗费大量的时间和内存。迭代则不需要反复调用函数和占用额外的内存。因此我们应该视不同情况选择不同的代码实现方式。
但是,我们上面的例子代码,没有使用到栈,那递归和栈有什么关系呢?其实,这样的递归问题是不需要用户来管理这个栈的,系统已经帮我们处理了。
栈的应用——四则运算表达式求值
这个知识点,需要使用很多图或动画才能说清楚,目前没有找到好用的做图、做动画软件。大家百度去了解吧,以后再补充。