2021-01-22

本文详细介绍了递归的概念、作用及执行过程,并通过实例解析了如何运用递归解决汉诺塔、N皇后问题以及计算逆波兰表达式。递归在算法中扮演重要角色,能够简化复杂问题的解决,如在汉诺塔中通过递归移动盘子,在N皇后问题中避免无效的排列尝试,以及在逆波兰表达式中实现运算的高效处理。
摘要由CSDN通过智能技术生成

算法学习日记(二)—递归(上)

一、基本概念

递归,就是在运行的过程中调用自己。一个函数调用其自身,就是递归。

二、作用

1、代替多重循环

2、解决本来就是递归定义的问题

3、将问题分解为规模更小的子问题进行求解

三、递归过程

以下列递归函数为例:

public static int Factorial(int n){
        if(n == 0)
            return 1;
        else
            return n*Factorial(n-1);
}

以为Factorial(4)例,函数执行用栈表示如下:

image-20210121145728475

Factorial(4)执行过程如下:

开始:Factorial(4) --> 4*Factorial(3) --> 3*Factorial(2) --> 2*Factorial(1) --> 1*Factorial(0) -->1*Factorial(0)返回 1*1 --> 2*Factorial(1)返回 2*1 --> 3*Factorial(2)返回 3*2 --> 4*Factorial(3)返回 4*6 --> 返回最终结果24.

四、例题

1、汉诺塔

image-20210121150859749

解题思路:

移动盘子过程如下:

(1)从A移动n-1个盘子到B;

(2)从A移动第n个盘子到C;

(3)从B移动n-1个盘子到C;

使用递归实现,

递归程序出口为n<=1;

题解代码:

public static void Hanoi(int n,char src,char mid,char dest){
        if (n == 1){
            //只需移动一个盘子
            System.out.println(src+"->"+dest);
            return;
        }
        Hanoi(n-1,src,dest,mid);    //(1)先将n-1个盘子从src移动到mid
        System.out.println(src+"->"+dest);//(2)在将一个盘子从src移动到dest
        Hanoi(n-1,mid,src,dest);  //(3)最后将n-1个个盘子从mid移动到dest
        return;
}

以四个盘子为例,测试:

image-20210121160112045

2、N皇后问题

image-20210122110248816

image-20210122110309963

皇后不会相互攻击的条件:即任意两个皇后都不能处于同一行、同一列或同一斜线上。

解题思路:

如果用枚举法,N皇后需要穷举NN种情况,到8皇后需要尝试88=16,777,216种情况,数量太过庞大,显然穷举法不是一种很好的解法,浪费了太多的计算资源在各种无用功上面。

对于N皇后问题,递归法是一种不错的解法,思路如下:

当我们选择了第一个皇后的位置之后,与其处于同行同列同斜线的位置便都无法被选择,第二个皇后只能放在未被第一个皇后所辐射到的位置上,接着放置第三个皇后,同样不能放在被前两个皇后辐射到的位置上,第四个皇后同样不能放在被前三个皇后辐射的位置上…若放置第N个皇后的时候,已经没有位置可选,则回退,重新放置N-1个皇后的位置。

使用递归回溯,每一次寻找下一个皇后作为递归过程,递归程序出口为每遍历完N行后。

**题解代码:**代码分成三个部分,第一部分寻找位置摆放皇后(find_Queen),第二部分检查该位置是否可以摆放(check),第三部分打印摆法(print)。

public class Ex_3 {

    //摆放皇后
    public static void find_Queen(int row,int N,int[][]Queen){
        if (row > N-1){
            print_queen(Queen,N);
            return;
        }
        for (int column = 0;column < N;column++){
            if (check(Queen,row,column,N)){
                Queen[row][column] = 1;
                //找下一个皇后
                find_Queen(row+1,N,Queen);
            }
            //位置不符,重新找上一个皇后
            Queen[row][column] = 0;
        }
    }

    //检查是否有位置
    public static boolean check(int[][] Queen,int row,int column,int N){

        //检查列
        for (int i = 0;i < row;i++)
            if (Queen[i][column] == 1)
                return false;
        //检查主对角线
        for (int i = row-1,j = column-1;i >= 0 && j >= 0;i--,j--)
            if (Queen[i][j] == 1)
                return false;

        //检查副对角线
        for (int i = row-1,j = column+1;i < N && i >= 0 && j < N && j >= 0;i--,j++)
            if (Queen[i][j] == 1)
                return false;

        return true;
    }
    //打印皇后
    public static void print_queen(int[][] Queen,int N){
        for (int[] arr:Queen) {
            for (int a : arr) {
                System.out.print((a == 1 ? "●" : "○")+" ");
            }
            System.out.println();
        }
        System.out.println("--------");
        Queen = null;
        Queen = new int[N][N];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入N");
        int N = sc.nextInt();

        int[][]Queen = new int[N][N];
        find_Queen(0,N,Queen);
    }
}

测试如下:

4皇后:

image-20210122141048873

8皇后(部分):

image-20210122141445336

3、逆波兰表达式

image-20210122142308438

image-20210122142329940

image-20210122142601715

注:实际逆波兰表达式是运算符在后面,这里以题目为准

递归终止条件为:最终返回的值是一个数

每次递归即为求一次二元运算,先读取运算符,然后读取两个逆波兰表达式,如果逆波兰表达式不是一个值,继续递归一次二元运算。

题解代码:

public class Ex_4 {

    private static String[] s;
    private static int index = -1;

    public static double exp(){
        index++;
        switch (s[index]){
            case "+": return exp() + exp();
            case "-": return exp() - exp();
            case "*": return exp() * exp();
            case "/": return exp() / exp();
            default: return Double.valueOf(s[index]);
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        s = new String[20];
        s = sc.nextLine().split(" ");
        System.out.println(exp());
    }
}

测试:

image-20210122145312934

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值