递归算法

(本文内容摘录自百度)

定义:

递归方法:在函数或子过程的内部,直接或者间接地调用自己的算法。
递归算法是把问题转化为规模缩小了的同类问题的子问题。然后 递归调用函数(或过程)来表示问题的解。
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).
 

特点

递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在 递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成 栈溢出等。所以一般不提倡用递归算法设计程序。
 

要求

递归算法所体现的“重复”一般有三个要求:
一是每次调用在规模上都有所缩小(通常是减半);
二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
三是在问题的规模极小时必须用直接给出解答而不再进行 递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。
 

如何设计递归算法

1.确定 递归公式
2.确定边界(终了)条件
 

递归过程

如果解决问题的方法是把一个问题分解成小的子问题,并且这些小的子问题可以用同样的算法解决,就可以用递归。
当分解到可以解决比较简单的子问题时分解过程即终止,我们将这些子问题称为终止条件。递归运用的是 “分而治之” 的策略。
递归算法的执行过程分递推和回归两个阶段
在递推阶段, 把较复杂的问题(规模n)的求解推到比原问题简单一些的问题(规模小于n) 的求解。
在回归阶段,当获得最简单情况的解后,逐级返回,依次得到较复杂问题的解。
 
如果一种算法的定义组成如下,则它就是递归的:
1)对应于某些参数可以求值的一个或多个终止条件
2)一个递归步骤,它根据先前某次值求当前值。递归步骤最终导致终止条件
 

实例 (内容来自 http://www.cricode.com/3489.html ):

1. Fibonacci 函数

斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
特别指出:第0项是0,第1项是第一个1。
这个数列从第二项开始,每一项都等于前两项之和。

斐波那契数列递归实现:
f(0) = 0;
f(1) = 1;
f(n) = f(n-2)+f(n-1); (当n>1时)

2. 汉诺塔问题

有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
每次只能移动一个圆盘;
大盘不能叠在小盘上面。
提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?

你汉诺塔问题的递归算法:
借助递归的思想,把n个圆盘问题简化为n-1个圆盘的问题
1) 将A上的n-1个圆盘, 借助C移到B
2)将A上最后哈一个圆盘移到C上
3)将B上n-1个圆盘借助A移到C上
move(int n, int from, int med, int to)

if (n==1) System.out.println(from+"--->" + to);
else {
     move(n-1, from, to, med);  //第一步
     System.out.printf(n+"从" + from+"移到"+to); //第二步
     move(n-1, med, from, to) //第三步

}

3. 二叉树遍历

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
遍历二叉树有以下3中方法:
1)先序遍历: 先访问根节点,然后遍历左子树,然后遍历右子树
2)中序遍历: 左子树--> 根节点 --> 右子树
3)后序遍历: 左子树 --> 右子树 --> 根节点

1)先序遍历

Status PreOrderVisit( BiTree T){
    if(T) {

       Visit(T->Data);
       if (PreOrderVisit(T->leftChild))
           if(PreOrderVisit(T->rightChild)) return OK;
       return Error;
     }  else return OK;
}

2. 中序遍历
Status MidOrderVisit(BiTree T){
    if(T) { 
       if(MidOrderVisit(T->leftChild) {
           visit(T->data);
           if(T->rightChild!=null)MidOrderVisit(T->rightChild)
                return OK
       }
        return ERROR;

   }  else return OK;
}

3. 后序遍历
Status PostOrderVisit(BiTree){
       if(T){
           if(PostOrderVisit(T->leftChild) {
               if(PostOrderVisit(T->rightChild)) {
                   visit(T->data);  
                   return OK;
               }
           }
           return ERROR;
       } else return OK;
}  

4. 字符串全排列

问题:

写一个函数返回一个串的所有排列。

解析:

对于一个长度为n的串,它的全排列共有A(n, n)=n!种。这个问题也是一个递归的问题, 不过我们可以用不同的思路去理解它。为了方便讲解,假设我们要考察的串是”abc”, 递归函数名叫permu。

思路三四参考: http://www.cricode.com/624.html 

思路一:
假设在长度为n的字符串中求m个字符的组合。
先从头扫描第一个字符,针对第一个字符,有两种选择
一种是把这个字符放到组合中去,接下来要在剩下的 n-1 个字符中选 m-1 个字符
一种是不把这个字符放到组合中去,接下来要在剩下的 n-1个字符中选m个字符

代码实现(java)

public class Test{

    public static void Combine(char[] c, int begin, int len, StringBuffer sb){
         if(len == 0) {
             System.out.print(sb + “ ”);
         }
         if (begin == c.len) {
             return;
         }

         sb.append(c[begin]);
         Combine(c, begin+1, len-1, sb);
         sb.deleteCharAt(sb.length()-1);
         Combine(c, begin+1; len, sb);

   }

    public static void main(String[] agrs) {
        String s = "abc";
        char[] c = s.toCharArray();
        StringBuffer sb = new StringBuffer();
        for (int i =1; i<=c.length; i++){
            Combine(c, 0, i, sb);
        }

    }

}

思路二:
为了提高效率,可以构造一个长度为n的01字符串(或二进制数)表示输出结果中是否包含某个字符,例如对字符串“abc”, 001 表示输出结果不含a, b, 只含c, 而101表示输出结果不含b, 只含ac. 
题目理解为输出 001 到 111这2^n-1个组合对应的字符串

思路三:我们可以把串“abc”中的第0个字符a取出来,然后递归调用permu计算剩余的串“bc” 的排列,得到{bc, cb}。然后再将字符a插入这两个串中的任何一个空位(插空法), 得到最终所有的排列。

我们可以把串“abc”中的第0个字符a取出来,然后递归调用permu计算剩余的串“bc” 的排列,得到{bc, cb}。然后再将字符a插入这两个串中的任何一个空位(插空法), 得到最终所有的排列。
比如,a插入串bc的所有(3个)空位,得到{abc,bac,bca}。 递归的终止条件是什么呢?当一个串为空,就无法再取出其中的第0个字符了, 所以此时返回一个空的排列。

思路四:

我们还可以用另一种思路来递归解这个问题。还是针对串“abc”, 我依次取出这个串中的每个字符,然后调用permu去计算剩余串的排列。 然后只需要把取出的字符加到剩余串排列的每个字符前即可。对于这个例子, 程序先取出a,然后计算剩余串的排列得到{bc,cb},然后把a加到它们的前面,得到 {abc,acb};接着取出b,计算剩余串的排列得到{ac,ca},然后把b加到它们前面, 得到{bac,bca};后面的同理。最后就可以得到“abc”的全序列。

 

 

5. 八皇后问题

转载于:https://www.cnblogs.com/daisy0707/p/5238039.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值