递归算法

来源:互联网 发布:团队介绍网页源码 编辑:程序博客网 时间:2024/05/07 14:22

http://blog.csdn.net/liuben/article/details/2223079

递归是设计和描述算法的一种有力的工具,它在复杂算法的描述中被经常采用。采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。(引自:http://blog.chinaunix.net/u/8780/showart.php?id=151086)   

  递归算法是解决问题的一种重要方法,它使许多复杂的问题变得简洁。递归的特点是:函数或过程调用它本身。使用递归的三个条件:
 1、计算数学模型具有递推关系;
 2、递归是有限次的;
 3、有结束条件。
 符合以上三个条件的问题即可使用递归算法。采用递归算法的程序结构清晰,可读性强,但时间和空间复杂性增大。二叉树和图遍历、二分排序和查找算法、数的阶乘、汉诺塔问题、斐波那契数列等等,都可以使用递归算法进行求解。下面介绍几个比较典型的递归算法,并给了相应的C例程。

1、排列生成算法
邻位对换法:下一个排列总是上一个排列某相邻两位对换得到的。 这个算法可描述如下: 
  对1—n-1的每一个偶排列,n从右到左插入n个空档(包括两端),生成1—n的n个排列。 
  对1—n-1的每一个奇排列,n从左到右插入n个空档,生成1—n的n个排列。 
  对[2,n]的每个数字都是如此。

#include <stdio.h>

void swap(char *a, char *b)
{
  char tmp = *a;
  *a = *b;
  *b = tmp;
}

void permStr(char* str,int i)
{
   if(i == strlen(str) - 1)
      printf("%s/n",str);
   else {
     int j;
     for(j = i;j < strlen(str);j++) {
       swap(&str[i],&str[j]);
       permStr(str,i + 1);
       swap(&str[i],&str[j]);
     }
  }
}

int main(int argc, char *argv[])
{
  permStr(argv[1], 0);
}

2、汉诺塔(Hanoi)问题
  汉诺塔(Hanoi)问题源自一个古老的传说:相传在古印度的一座神庙前,有一根串着64个祭神用的圆盘的柱子,这些圆盘是按大小顺序叠放的,大的在下,小的在上,僧侣们要将这些圆盘借助一个柱子移到另一个柱子上,移动过程中,一次只能移动一个,并且要始终保证每个柱子上的圆盘大的在下,小的在上,什么时候移完,就意味着世界末日。三根柱子,n个圆盘在第一根柱子上,要全部移到第三根柱子上.而且很容易推出,n个圆盘时,最小移动次数为2^n-1次.

#include <stdlib.h>
int c = 0;

void move(char x, int n, char z)
{
  printf("%i. Move disk %i form %c to %c/n", ++c, n, x, z);
}

void hanoi(int n, char x, char y, char z)
{
  if (n == 1)
    move(x, 1, z);
  else {
    hanoi(n-1, x, z, y);
    move(x, n, z);
    hanoi(n-1, y, x, z);
  }
}

int main(int argc, char *argv[])
{
  int n = atoi(argv[1]);
  hanoi(n, 'X', 'Y', 'Z');
}

3、n!尾部0的个数问题
 经推理可得0的个数等于5因子的个数,即有:f(n) = n/5 + f(n/5), f(0) = 0。例如f(100) = 100/5 + 20/5 = 24。

int zero(int n) {
  if (n == 0)
    return 0;
  else
    return (n/5 + zero(n/5));
}

4、上楼问题
 某楼梯有n(n>=1)阶台阶,某人一步最多迈m(n>=m>=1)阶。问:有多少种不同的方案上楼?
经分析可以得到如下递归模型:
 f(n) = f(n-1) + f(n-2) + ... + f(n-m),
  f(0) = 1, f(1) = 1, f(2) = 2,
  f(k) = f(1) + f(2) + ... + f(k-1)  其中,k<=m。

  特别地,当m=2时,为斐波那契数列,即:
  1 1 2 3 5 8 13 21 34 55 89 ...
  f(n-1) / f(n) -->趋于黄金分割0.618

int upstairs(int n, int step) {
  int i, sum = 0;

  if (n == 0 || n == 1)
    return 1;
  else if (n == 2)
    return 2;
  else if(n > 2 && n < step) {
    for(i=1; i<n; i++)
      sum += upstairs(i, step);
    return sum;
  } else {
    for(i=1; i<=step; i++)
      sum += upstairs(n-i, step);
    return sum;
  }  
}

int main(int argc, char *argv[]) {
  int n, m, sum;

  n = atoi(argv[1]);
  m = atoi(argv[2]);
  sum = upstairs(n, m);
  printf("upstairs(%d, %d) = %d/n", n, m, sum);
}


5、集合划分问题
 设S是一个具有n个元素的集合S = {a1, a2, ..., an},现将S集合划分为K个满足下列条件的子集s1, s2, ..., sk。
(1)si不能为空;(2)si与sj交集为空;(3)所有子集的并集为S。
问:有多少种划分?
经分析可以得到如下递归关系:
 s(n,k) = s(n-1, k-1) + k * s(n-1, k),
  s(n,k) = 0 , 若 n<k 或 k=0,
  s(n,k) = 1 , 若 k=1 或 k=n。

int sets(int n, int k) {
  if (n < k || k == n)
    return 0;
  else if (k == 1 || k == n)
    return 1;
  else
    return (sets(n-1, k-1) + k * sets(n-1, k));
}


(刘爱贵 / Aiguille.LIU, 2008-03-27)

原创粉丝点击