• 算法学习笔记——洗碗时遇到的汉诺塔问题


         前几天去医院体检的时候带了《漫画算法》这本书,在排队的时候看了一些面试中的算法的,再三纠结要不要重复造一回轮子写一些笔记的,然后今天就发现自己快要忘光了......

      犹豫就会败北,下次有时间一定会补上。(又是下次一定?)

    一、问题描述

      下面回到正题上来,今天中午洗碗的时候遇到5个盘子和碗,大小依次递减,于是洗完之后我开始玩起了汉诺塔......

      我们看看百度对汉诺塔问题的描述:

       

      如果你不能理解上面的文字:这里我送上一份李永乐老师的讲解视频

      https://www.bilibili.com/video/BV1gJ41177fX?from=search&seid=13257433899054341699

     

    二、什么是递归

      看过视频之后,相信大家对于这个游戏怎么玩已经比较清楚了,但是你可能还不清楚什么是递归。

      这里我再举一个经典例子:计算n的阶乘

      阶乘的定义式:n!=n*(n-1)*(n-2)*···*3*2*1;

      

      仔细的看一下定义式,聪明的你可能会看出来,我们可以将上述定义式写成如下样子:

      n! = n * [ (n-1)! ] ; ①

      (n-1)! = (n-1) * [ (n-2)! ] ;② ······依次类推,我们会得到

      2! = 2 * [ 1! ] ;  ③而

      1! = 1 ;④

      所以只要我们从【1!】倒回去计算就可以得到【n!】了,这就是递归思想

    三、递归的梳理

      如果你理解了递归的思想,你可能就会发现对于递归问题我们需要找到的有:

        1)我们先是得到一个递推式(如上面的①式和②式),我们管它叫递归体

        2)根据递推式找到递推的终点(也就是④式),我们管它叫递归头

        3)然后从递归头开始回溯(就是代入到递推式中去),最后得出结果

      这就是我对于递归思想的梳理。

    四、上代码

      如果你理解上面的例子,那么我们就来动手写一写代码;下面是我给出来的代码: 

    //递归计算n的阶乘
    public class Recursion{
        public static void main(String[] args) {
            int result1 = fn(3);//6
            System.out.println("计算结果是" + result1);
            int result2 = fn(5);//120
            System.out.println("计算结果是" + result2);
            int result3 = fn(10);//3628800
            System.out.println("计算结果是" + result3);
    
            //这里无法得到结果是爆栈了,超出int的数据范围,
            //fn(100)连long都装不下,可以考虑使用BigInteger处理,这里不做演示
            //所以在日常使用中要注意栈溢出问题!!!!!!!
            int result4 = fn(100);
            System.out.println("计算结果是" + result4);
        }
    
        public static int fn(int n){
            if(n != 1){//这里就是递归体
                return n * fn(n-1);
            }else{//这里就是递归头
                return 1;
            }
    
        }
    }

      

      如果上面的没问题了,我们可以回到正题上来了——汉诺塔问题的代码:

      (如果不知道怎么写可以返回去再看一遍李永乐老师的思路讲解,然后利用三个盘子的情形来实现)

      

    //汉诺塔问题的代码实现
    public class Recursion1 {
        public static long count = 0;//定义成一个公共属性,可以计算移动操作的次数
        public static void main(String[] args) {
            
            for(int i = 1; i <=5; i++){
                System.out.println(i+"个盘子的汉诺塔操作步骤:");
                hanoi('a', 'b', 'c', i);
                System.out.println("共移动了【" + count + "】次");//结果=2的i次方减1
                count = 0;//别忘了置零,不然会累加
                System.out.println();
            }
            
            System.out.println("检验电脑性能的时候(作死)的时候到了!");
            hanoi('a', 'b', 'c', 32);
            System.out.println("计算完成");//为了验证你的电脑是不是还在计算中,反正我的电脑算不出来64个盘子
            System.out.println("共移动了【" + count + "】次");//4294967295
        }
    
        /**
         * 将n个盘子从a柱子通过b柱子移动到c柱子上
         * @param n 盘子的个数
         */
        public static void hanoi(char a,char b,char c,int n){
            if(n == 1){//递归头
                move(a, c);//将第一个盘子从a直接移动到c
            }else{//递归体
                hanoi(a,c,b,n-1);//将n-1个盘子从a柱通过c柱移动到b柱
                move(a, c);//将第n个盘子从a柱直接移动到c柱
                hanoi(b, a, c, n-1);//将n-1个盘子从b柱通过a柱移动到c柱
            }
        }
        public static void move(char a,char c){
            //为了单纯得到操作次数所以将输出语句注掉了
            //System.out.println("将" + a + "上的盘子移动到" + c);//将最大的盘子从a直接移动到c
            count++;//每移动一次,计数加一
        }
    }

      好了,以上就是本期的全部内容了。如果想得到运算时间的同学可以在for循环中利用System.currentTimeMillis()来统计,这里就不做演示了。

      感谢大家的查阅,写的不太好,希望收到大家的意见和建议。

  • 相关阅读:
    定时删除日志文件---linux定时清理日志
    Packagist 镜像使用方法--composer
    laravel 5.5 跨域问题解决方案
    linux服务器上面部署ShowDoc 安装Composer
    shell之批量新增用户脚本(http-basic-auth)
    js转义问题
    js之select三级联动
    《远见》之读书笔记
    Node.js之判断字符串中是否包含某个字符串
    微信小程序之页面传参
  • 原文地址:https://www.cnblogs.com/xzhm/p/12673708.html
Copyright © 2020-2023  润新知