【算法入门系列】递归

前言:不知道有没有小伙伴和我一样,在学习算法的路上,递归始终是一只拦路虎,不知道该如何去写一个递归。我在学习汉诺塔问题时我始终想不明白汉诺塔问题是怎么通过递归实现的,本文就以汉诺塔为例,解决递归这只拦路虎。

文章目录

    • 一、什么是递归?
    • 二、递归能干什么?
    • 三、递归怎么用?
    • 四、汉诺塔问题

一、什么是递归?

递归(recursion)的定义是在定义一个过程或函数时出现调用本过程或本函数的成分。

在递归中比较经典的一个例子就是:从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么呢’"…

递归简单来说就是自己调用自己。

二、递归能干什么?

递归算法通常把一个大的复杂的问题层层分解为一个或多个与原问题相似的规模较小的问题(就像洋葱一样)来求解,递归策略只需要少量的代码即可解决多次重复计算,大大减少了算法的代码量。

举个例子:

  • 阶乘就符合上述描述,3!代表着1*2*3,但是如果用递归来描述阶乘3!则为3*2!2!=2*1!,1!=1,因为2!与3!类似,只是规模较小,符合上述描述。经过层层调用,最后1!=1时即可停止递归,计算出最终结果。

    public class recursions {
        public static void main(String[] args) {
            int result = recursion(3);
            System.out.println(result);
        }
        public static int recursion(int a){
            //当a=1时,1!=1
            if (a==1){
                return 1;
            }
            //a=3时,multi=3*recursion(2)即3*2!
            //a=2时,multi=2*recursion(1)即2*1!
            int multi = a*recursion(a-1);
            return multi;
        }
    }
    

三、递归怎么用?

  1. 一般来说,能够用递归解决的问题应该满足两个条件:
  • 递归要解决的问题能够转化为一个或多个子问题来求解,并且子问题的计算过程与原问题完全相同,只是数量规模不同。
  • 递归的调用次数是有限的,即递归必须有结束递归的条件来结束递归,否则程序将一直运行下去。

需要注意的是结束递归的条件必须要写在递归之前,防止无限递归陷入死循环。

  1. 那么一个完整的递归应该是什么样子的呢?
  • 首先确定结束条件。

  • 其次要有递归,即将问题分解。

public void recursion(参数1){
	if(结束条件){
		return xxx;
	}
	recursion(参数2);
}

这样一个完整的递归就写出来了,接下来我们来看一个递归的经典例子:汉诺塔问题

四、汉诺塔问题

如果不知道汉诺塔问题的小伙伴可以看这里汉诺塔

在用递归解决该问题时,首先我们要明确我们的目的是描述出移动的步骤,因此我们需要A,B,C三个形参作为柱子,其次我们如何知道有多少个盘子需要移动呢?因此需要一个常数值来代表盘子的个数。

public static void hanoi(int n, char A, char B, char C) {
    /**
    总体思路:
     1. 以C盘为中介,从A杆将1至n-1号盘移至B杆;

     2. 将A杆中剩下的第n号盘移至C杆;

     3. 以A杆为中介,从B杆将1至n-1号盘移至C杆。
     */
    if (n == 1) {
        //如果只有一个,直接从A移动到C即可
        System.out.println("从" + A + "移动到" + C);
    } else {
        //表示先把n-1个圆盘成功从A移动到B
        //划重点:该方法表示从第二个参数(形参A)移动到第四个参数(形参C),因此如果从A移动到B应该是A,C,B的顺序
        hanoi(n - 1, A, C, B);
        //把第n个圆盘从A移动到C
        System.out.println("从" + A + "移动到" + C);
        //表示把n-1个圆盘再成功从B移动到C
        hanoi(n - 1, B, A, C);
    }

前文中我们说过递归是将一个复杂的问题分解为一个或者多个子问题来求解,这里的汉诺塔问题明显属于后者,因为每次递归都有三个步骤,因此我用剥洋葱来类比也不太符合哈哈,但我是这么理解的,不知道这么理解有没有问题。

建议大家在脑袋中根据程序过一遍这个流程,不是说让你在脑海中复现移动64个盘子的过程(我觉得一般人做不到),你可以尝试从3个盘子开始,多推演几遍,不仅可以考验记忆力,还可以增强对该解法的理解程度。

总结:汉诺塔问题是令我一直的不解的一个问题,之前不理解递归是一方面,我觉得主要问题是不知道这个程序该以什么形式写,不清楚是描述该过程还是真的假设三个柱子,然后将盘子进行移动,今天终于终结了汉诺塔这个难题,同时也希望本文对你有所帮助。同时文中出现的问题也欢迎大家批评指出,共同进化。​

活动地址:CSDN21天学习挑战赛

你可能感兴趣的:(算法与数据结构,算法,java,c++)