算法之递归与回溯

递归
递归是指函数/过程/子程序在运行过程中直接或间接调用自身而产生的重入现象。

运用递归的条件:
1. 子问题须与原始问题为同样的事,且更为简单;
2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。

就比如经典的汉诺塔问题:
共有3根柱子ABC,A柱上有若干个圆盘(从大到小依次摆放,最小的在最上方),大盘子不能放在小盘子的上面,只能依次移动盘子,问如何将A柱上的圆盘移动到C柱上(顺序与A柱相同)?

可以将过程分为3个步骤:
第一步:将除最大盘子以外的n-1个盘子移动到B。(可以理解为将n-1个盘子从A移动到B,然后进行递归计算)
第二歩:将最大的盘子移动到C。(可以直接完成)
第三步:将B柱上的n-1个盘子移动到C。(重复前两步,将除最大盘子以外的盘子移动到A,再将最大的盘子移动到C。)

public class 递归算法_汉诺塔问题 {
     public static void main(String[] args) {
    System.out.print("输入要移动的个数:");
    Scanner scanner = newScanner(System.in);
    int num = scanner.nextInt();
    Hanoi h = new Hanoi();
    h.hanoi(num,'A','B','C');
     }
}
class Hanoi{
    void move(char a,char b){
        System.out.println(a+"----->"+b);
    }
    //n为所需要移动盘子的个数,abc表示柱子
    void hanoi(int n, char a,char  b,char c){
        //出口
        if (n == 1) {
            move(a, c);
        }else{
            //移动除最下面以外所有盘子到b(c为中介)
            hanoi(n - 1, a, c, b); 
            //移动最下面的盘子到c
            move(a, c);
            //移动剩下的盘子到c(在b上,a为中介)
            hanoi(n - 1, b, a, c);

        }
    }
}

首先看一下运行结果:
这里写图片描述

还有一个经典例子,四皇后问题:(没有步骤,直接上代码)
这里是用一维数组存放的方法:

  1. 首先用一维数组来记录皇后放置位置。
  2. 数组内的每个数组元素可取1—4 4个值。数字就表示皇后所放置的位置。
  3. 从第一个元素开始,取值为1,然后进行递归,判断第二个元素的取值,如果可以进行取值,则再进行递归,否则返回并取下一个值。
  4. 判断条件,每个元素所取的值不能有相同的,并且2个元素之间所取值的大小不能等于数组下标的差值,即所取值不能在对角线上。
public class 递归算法_四皇后问题_一维数组 {
    public static void main(String[] args) {
        Squeue squeue = new Squeue();
        squeue.fun(0);      
    }
}

class Squeue{
    //存放结果的数组
    int que[] = new int[4];
    //打印最终的结果
    void display(){ 
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (j == que[i] - 1) {
                    System.out.print(1+" ");
                }else {
                    System.out.print(0+" ");
                }
            }
            System.out.println();   
        }
        System.out.println();
    }
    //出口,如果找到满足条件的4个皇后,输出
    void fun(int a){
        if (a == 4) {
            display();
            return;
        }
        //取值为1--4
        for (int k = 1; k <= 4; k++) {
            boolean ct = true;
            que[a] = k;
            //判断当前位置所放置的皇后是否满足条件
            for (int i = 0; i < a; i++) {
                if (Math.abs(que[a]- que[i]) == a- i ) {//在对角线上
                    ct = false;
                }else if (que[a] == que[i]) {//同行
                    ct = false  ;       
                }
            }
            //如果满足则进行递归,不满足则继续循环
            if (ct) {
                fun(a+1);
            }
        }       
    }
}

结果如下图:
这里写图片描述

回溯
回溯也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。

用 Icossian 问题来进行说明:

找出所有能遍历所有的城市的路径,且每个城市只能经过一次。
这里写图片描述

由题可知有5个城市,则可设置一个5*5的数组用来存放路径关系。用0—1表示2个城市之间是否相连。
则图可转变为下列数组
这里写图片描述

将行看做是出口,列看做入口。
例如从A出发可达到B,则将A所在的行内的1变为2(关闭)。将B所在的列内的变为2(关闭)。然后将B所在的列变成行(入口变出口 )进行递归,找寻下一次满足条件的城市。如果存在则递归,反之跳回并将数据恢复到上一步。
当a[]数组的元素达到6个且A为结束点时就输出。

public class 回溯算法_Icossian问题 {
    public static void main(String[] args) {
        Country country = new Country();
        country.setCountry();
        country.visit(0, country.c);
    }
}
class Country{
    int c[][] = new int [5][5];//记录各点之间的关系
    char a[] = new char[6];//记录可行路径
    int count = 1;//路径放入的地方 a[count]
    void setCountry(){  //输入各点之间之间的关系       a[0] = ‘A’;//从A开始
        Scanner scanner = new Scanner(System.in);
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                c[i][j] = scanner.nextInt();
            }
        }
    }

    void visit(int h, int c[][]){
        //出口  走过所有城市且最后到达A城
        if (count == 6 && a[count - 1] == 'A') {
            display(a);
            System.out.println();
        }
        //寻找当前城市与其他城市之间的通路
        for (int i = 0; i < a.length-1; i++) {
            //如果存在通路
            if (c[h][i] == 1) {
                a[count] = (char)('A' + i);
                count++;
                for (int j = 0; j < a.length-1; j++) {
                    if (c[j][i] == 1) {
                        c[j][i] = 2;//将通路关闭(表示路已经走过了)
                    }
                }
                //进行递归
                visit(i, c);
                //以下进行回溯,当当前情况递归完毕后,要还原当前情况下的状态
                count--;
                for (int j = 0; j < a.length-1; j++) {
                    if (c[j][i] == 2) {
                        c[j][i] = 1;
                    }
                }
            }
        }
    }
    void display(char a[]){
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]);
        }
    }
}

运行结果:
这里写图片描述

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值