【浅谈递归(二)】常见递归问题的分析与解答

一、经典递归问题

1、汉诺塔问题

题目描述:古代有一个梵塔,塔内有三个座 A、B、C,A 座上有 64 个盘子,盘子大小不等,大的在下,小的在上。有一个和尚想把这 64 个盘子从 A 座移到 B 座,但每次只能允许移动一个盘子,并且在移动过程中,3 个座上的盘子始终保持大盘在下,小盘在上。在移动过程中可以利用 B 座,要求打印移动的步骤。如果只有一个盘子,则不需要利用 B 座,直接将盘子从 A 移动到 C。
现在有 n 个圆盘从上往下从小到大叠在第一根柱子上,要把这些圆盘全部移动到第三根柱子要怎么移动呢?请找出需要步骤数最少的方案。

分析:现在我们要将 n 个盘子移到第三个塔上,假如我们将 n-1 个移到第二个。再将剩下的一个移到第三个。最后把 n-1 个移回第三个上。
令 Hanno(n,Ta,Tb Tc) 表示借助 Tb 塔,把 n 个盘子,从 Ta 塔移到 Tc 塔。那么根据问题分解可知

Hanno(n,Torigin,Temp,Tdes)可以分解为
Hanao(n-1,Torigin,Tdes,Temp); //借助Tdes,把n-1个盘子,从Torigin移到Temp
move(1,Torigin,Tdes);//把剩下的一个盘子移到Tdes
Hanno(n-1,Temp,Torigin,Tdes)//记住Torigin,把Temp中剩下的n-1个盘子移到Tdes,至此问题得以解决。
三步实现,问题已经解决
当n=1时,直接移动,move(Ta,Tc)//结束条件

实现代码

#include
using namespace std;
int i=0;
void move(char Ta,char Tb){
    cout<<"第"<"步:"<<"盘子从"<"----->"<void Hano(int n, char Torigin, char Temp, char Tdes){
    if(n==1){
        move(Torigin,Tdes);
    }else{
    Hano(n-1,Torigin,Tdes,Temp);
    move(Torigin,Tdes);
    Hano(n-1,Temp,Torigin,Tdes);
    }
}
int main(){
    Hano(3,'A','B','C');
    return 0;
}

【浅谈递归(二)】常见递归问题的分析与解答_第1张图片

2、集合子集问题

题目描述:已知一集合, 求该集合的所有子集。例如集合 {a,b,c} 的所有子集合为 {},{a},{b},{c},{a,b},{a,c},{b,c},{a,b,c}。

分析:求一个集合的子集,此问题不太好分为求规模较小元素的子集问题,但是我们可以考虑每一个元素。也就是每一个元素取或者不取的问题,这也是一个递归的过程。

#include
using namespace std;
int i=0;
void subColl(string str, int index,string temp){
    if(index==str.length()){   //结束条件
        cout<else{
        subColl(str,index+1,temp);   //不取第index个元素
        temp = temp+str[index];
        subColl(str,index+1,temp);   //取index个元素
        temp=temp.substr(0,temp.length()-1);  //回溯
    }
}
int main(){
    string str = "abcd";
    subColl(str,0,"");
    cout<<"总共"<"个结果"<

【浅谈递归(二)】常见递归问题的分析与解答_第2张图片

3、字符串全排列问题

问题描述:输入一字符串 (要求不存在重复字符),打印出该字符串中字符中字符的所有排列。
例如:输入 “abc”,输出结果为 abc, acb, bac, bca, cab 和 cba。

分析:
当只有一个元素时”a”, 其组合就为”a”
当再加一个元素”b” 时,”b” 的插入位置有两个,得到”ab”,”ba”。
在是那个一步的基础之上再加一个元素,则每一个都有三个插入位置,”cab”,”acb”,”abc”,”cba”,”bca”,”bac”。
由此我们可以看出此程序递归过程不是一棵二叉树,其后面的树有多个分支。

代码:

#include
using namespace std;
void compose(string str,int index,string temp){
    if(str.length()==index){
        cout<else{
        for(int i=0;i1;i++){
                string s="";
            if(i!=temp.length())
                 s = temp.substr(0,i)+str[index]+temp.substr(i); //插在中间位置
            else
                 s = temp+str[index];    //插在末尾
            compose(str,index+1,s);
        }
    }
}
int main(){
    string s = "abc";
    compose(s,0,"");
}

【浅谈递归(二)】常见递归问题的分析与解答_第3张图片

4、机器人寻路问题

问题描述:设想机器人坐在 X*Y 的网格之中,只能向左,向下移动。路径中有些点机器人不能踏足,设计一种算法,找出一条路径,让机器人从左上角走到右下角。

分析:机器人在当前位置,那么其只可能向右走、和向左走。于是我们可以正向思考问题,在当前位置一步一步分解,机器人所走的过程可以看成一棵二叉树。此题因为不要找出所有路径,只需要找到一条,所有可以采用剪枝的方法,使得搜索树的搜索范围大大减少。

    public static boolean slove3(int[][] array,int m,int n,int x,int y){
        if(x==m-1 && y==n-1 && array[x][y]!=1){
            path.add(new Point(x,y));
            return true;
        }
        if((x+1array[x+1][y]!=1)) {
            path.add(new Point(x,y));
            if(slove3(array,m,n,x+1,y)) return true;
            path.removeLast();
        }
        if((x1array[x][y+1]!=1){
            path.add(new Point(x,y));
            if(slove3(array,m,n,x,y+1)) return true;
            path.removeLast();
        }
        return false;

2

二、递归函数写法的一些技巧

1、如果要求求所有的路径,按照正常的模式写,当声明 void 时候,调用到函数结束因为没有 return 语句,所以要用 if…..else….. 来限制执行,见问题 1、2、3。
2、如果要求求解一个解,则可以用剪枝的方法减少搜索过程,加快效率,如问题 4 采用的方法。
3、如果一眼看不出子问题时候,可以使用简单构造法来寻找规律,就是先假设较少的情况,在依次推广。
4、分析问题右自上而下的方法,也就是从整体来分解子问题求解,也有自下而上的方法,就是从简单的情况推广到一般情况。

你可能感兴趣的:(数据结构,递归,递归,c,语言)