递归

递归

  从程序设计的角度看,递归是一种程序设计方法。函数直接或间接地调用自身,称为递归调用。递归调用是用相同的策略去解决规模更小的问题,直至问题规模达到某个边界条件时,不再进行递归调用,而是直接处理。

  函数递归调用的嵌套层数称为递归层次。其中,其他函数对递归函数的调用为第0层,递归函数第一次调用自身为第1层......以此类推。递归函数的执行过程为:

  1. 执行第n-1(n>0)层的代码到调用递归函数时,进入第n层,执行第n层递归函数的代码。

  2. 第n(n>0)层递归函数的执行结果返回到第n-1层,之后执行第n-1层调用递归函数之后的代码。

  编写递归函数时需要注意:

  1. 递归函数传入引用类型的数据时会改变数据本身。

  2. 递归必须设置退出条件,而且每次递归时都是向退出条件逼近,否则就变成了无限递归,最终会出现StackOverflowError。

阶乘

  阶乘的定义是:n! = 1 * 2 * 3 * ... * n。特别的有,0! = 1。

  根据阶乘的定义,可以得到n! = n * (n - 1)!。也就是说,n的阶乘可以通过先求n - 1的阶乘再乘以n得到。所以可以通过递归实现求一个数的阶乘,而递归的退出条件即为n == 0时返回1。

1 public static int factorial(int n) {
2     if (n == 0) return 1;
3     return n * factorial(n - 1);
4 }
factorial

最大公因数

  求a和b(a ≥ b)的最大公因数的过程为:

  a = b * q1 + r1

  b = r1 * q2 + r2

  r1 = r2 * q3 + r3

  ...

  rn-2 = rn-1 * qn-1 + rn

  rn-1 = rn * qn

  可以发现,上一个式子的除数和余数分别是当前式子的被除数和除数。直到出现一个式子的余数为0时,该式子的除数即为最大公因数。这种求最大公因数的方式称为辗转相除法。

  特别的,任何一个数和0的最大公因数都是这个数本身。

  可以通过递归实现辗转相除法求最大公因数,而递归的退出条件即为除数为0,此时被除数为最大公因数。

1 public static int gcd(int a, int b) {
2     if (a < b) return gcd(b, a);
3     if (b == 0) return a;
4     return gcd(b, a % b);
5 }
gcd

斐波那契数列

  1, 1, 2, 3, 5, 8, 13, ...这个数列被称为斐波那契数列。斐波那契数列的特点是:从第3项开始,当前项为前两项之和。所以斐波那契数列的递推公式为:F(n) = F(n - 1) + F(n - 2)。

  求斐波那契数列第n项的数值,可以转变为求其第n-1项和第n-2项的数值之和。可以通过递归来实现该功能,而退出条件即为n == 1和n == 2时返回都为1。

1 public static int fibonacci(int n) {
2     if (n == 1 || n == 2) return 1;
3     return fibonacci(n - 1) + fibonacci(n - 2);
4 }
fibonacci

汉诺塔问题

  汉诺塔游戏(http://www.4399.com/flash/109504.htm#sim2|109504):有三根柱子,其中第一根柱子上有n个圆环,越往下的圆环越大。

  游戏任务:将第一根柱子上的所有圆环移动到第三根柱子上。

  游戏规则:每次移动都是移动最上面的圆环,且必须保证每次都是小环在上大环在下。

  例如,移动3个圆环,效果如图所示:

  

  移动n(n > 0)个圆环的一般步骤如下:

  a. 将n - 1个圆环从第一根柱子移到第二根柱子。

  b. 将第n个圆环从第一根柱子移动到第三根柱子。

  c. 将n - 1个圆环从第二根柱子移动到第三根柱子。

  所以,可以通过递归实现汉诺塔。而递归退出的条件则为n == 1时结束。

 1 public static void hanoi(int n, char a, char b, char c) {
 2     if (n == 1) {
 3         // 1个圆环,直接从第一根柱子移动到第三根柱子
 4         System.out.println("第" + n + "个圆环从第" + a + "根柱子移动到第" + c + "根柱子。");
 5     } else {
 6         // 将n - 1个圆环从第一根柱子移动到第二根柱子上
 7         hanoi(n - 1, a, c, b);
 8         // 将第n个圆环从第一根柱子移动到第三根柱子上
 9         System.out.println("第" + n + "个圆环从第" + a + "根柱子移动到第" + c + "根柱子。");
10         // 将n - 1个圆环从第二根柱子移动到第三根柱子上
11         hanoi(n - 1, b, a, c);
12     }
13 }
hanoi

  测试3个圆环的输出结果:

  

八皇后问题

  八皇后游戏(http://www.7k7k.com/swf/49842.htm):有一个8*8的棋盘和8个皇后棋子

  游戏任务:在棋盘上摆放8个皇后。

  游戏规则:摆放的8个皇后不能相互攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

  摆放第n个皇后的一般步骤如下:

  a. 摆放在第n行的位置上。

  b. 判断是否可以摆放在这个位置上,即摆放后是否依然满足皇后不会相互攻击。

  c. 如果第n行上没有一个位置使得n个皇后不会相互攻击,证明第n - 1个皇后摆放的位置不正确,需要改变位置。

  八皇后问题可以通过递归实现,递归的退出条件为成功摆放最后一个皇后。

1 public static boolean check(int[] bound, int x) {
2     for (int i = 0; i < x; i++) {
3         // bound[i] == bound[x]表示两个皇后在同一列,x - i == |bound[x] - bound(i)|表示两个皇后在同一斜线
4         if (bound[i] == bound[x] || x - i == Math.abs(bound[x] - bound[i])) return false;
5     }
6     return true;
7 }
check
 1 public static int put(int[] bound, int n) {
 2     int count = 0;
 3     for (int i = 0; i < 8; i++) {
 4         bound[n] = i;
 5         if (check(bound, n)) {
 6             // n == 7表示已经成功摆上最后一个皇后,该条件作为递归的退出条件
 7             if (n == 7) {
 8                 count++;
 9                 for (int index : bound) 
10                     System.out.print((index + 1) + " ");
11                 System.out.println();
12             } else {
13                 count += put(bound, n + 1);
14             }
15         }
16     }
17     return count;
18 }
put

  其中,check()方法用于判断摆放皇后的位置是否正确;put()方法用于计算8皇后摆放方案。

  通过测试,得到结果如下:

  

  可以看到,八皇后一共有92种摆法。以“5 1 4 6 8 2 7 3”为例,“5”表示第一个皇后摆放在第1行的第5个位置,“6”表示第四个皇后摆放在第4行的第6个位置......

  

posted on 2019-09-19 11:19  寇德·坡特  阅读(322)  评论(0编辑  收藏  举报

导航