菜鸟更要独立思考之一 从思路到实现,菜鸟也能掌握递归算法

在菜鸟眼中的递归算法是怎样的?

1.从视觉(形式)上:简洁

2.从感觉(体会)上:牛逼

3.从知觉(使用)上:踟蹰

总结起来就是看起来简单,算法很牛逼,自己用起来不顺手,看了答案之后才觉得:原来是这么回事,我就差那么一丢丢

以汉诺塔入题

汉诺塔

有A、B、C三个柱子,将N个按上小下大顺序呢叠放的圆盘,从A柱移动到C柱
要求:一次只能移动一个圆盘
            小圆盘上不可放大圆盘

不拘泥于答案去思考

当初学算法的时候,老师给出过答案,网上也有很多高手给出了汉诺塔问题答案,但是,说实话,看的快,忘得也快,归根到底,没懂!

那什么是懂?

懂就是流程化,标准化,当你拿到一个问题,心中123就已经出来,你只需要去分别完成123的时候,这就是懂了。

流程化的思路才是懂了。

流程化的汉诺塔解决思路

递归含义飘过

递归算法就是将大问题拆成若干同类的小问题,直到小问题被解决。

一定要动手

对于一个N的问题,菜鸟一定要去动手画一画,列出前几项,就像找规律一样

这里,我用{1,2,3,4},没有特殊含义,只是将1234看做一个整体

//以n=4为例:
move{1,2,3,4} from A→C
        move{1,2,3} from A→B
                move{1,2} from A→C
                        move{1} from A→B
                        move{2} from A→C
                        move{1} from B→C
                move{3} from A→B
                move{1,2} from C→B
        move{4} from A→C
        move{1,2,3} from B→C
                move{1,2} from B→A
                        move{1} from B→C
                        move{2} from B→A
                        move{1} from B→A
                move{3} from B→C
                move{1,2} from A→C
                        move{1} from A→B
                        move{2} from A→C
                        move{1} from B→C

递归关键

大小问题是同类的,那我们分析问题也要如此,如果你能够将不同的问题,找到一个角度去观察,它们的形式一致,那么你已经成功了一半。

比较N=4与N=2

move{1,2,3,4} from A→C
        move{1,2,3} from A→B
        move{4} from A→C
        move{1,2,3} from B→C

 

                move{1,2} from A→C
                        move{1} from A→B
                        move{2} from A→C
                        move{1} from B→C

看到这里,是不是内心有什么东西被触动了,就像当你第一次看到别人的递归算法答案时,这么调用,在继续自己调用自己。。。

是的,我不光第一次看到别人打答案,第二次,第三次,甚至于每次遗忘重新拾起的时候,都是同样的感受。

成功了50%

我们已经找到了一个角度,它以同样的形式将N=4和N=2时的问题展现在我们面前,剩下50%就是将思路转换为代码

剩下50%,算法到代码

对于菜鸟而言,即便是算法给你了,转换为代码也是相当头疼的一件事

很重要的两句话,一眼可见底的步骤是最小单位,用f1()表示,将一眼不可见底的步骤用f()表示,在步骤外面套一个f()

如此一来,汉诺塔问题的递归算法代码实现起来就是

        f(){

                f();

                f1();

                f();

        }

为了好看一点我们用move(n,from,to)来代替f1(),对应上面的move{1} from A → B,它是一眼可见底的

用hanoi(n,from,to)来代替f(),对应于上面的move{1,2,3} from A → B,它一眼不可见底,你不可能凭空说出如何把123按照游戏规则从A移动到B

一级美化之后,代码变为

        hanoi( n, from, to){                        //对应于move{1,2,3,4} from A → C

                hanoi( n, from, to);                //对应于move{1,2,3} from A → B

                move( n, from, to);               //对应于move{4} from A → C

                hanoi( n, from, to);               //对应于move{1,2,3} from A → C

       }

有时候,你需要做点递归外的工作

细心的人会发现,不对啊,内部有三个字母ABC,参数却只有两个,泥坑我啊。

是的,因此我们需要在hanoi参数中再加一个参数,这个参数你要加在外层的f()中,然后内层依据外层处理

按照大众做法来,我们放在中间吧,免得这里一套那里一套,省去读者转换思考的工作

继续美化代码

        hanoi( n, from, mid, to){                //添加后,我们将N=4时记作 hanoi( 4, A, B, C);

                                                                   //以外层为主,我们将在from to的中间了,注意,这样一来,就是说第一个参数代表起点,第二个辅助点,第三个重点

                                                                  //无论你的标识怎么变,递归是由外而内的,内部意义依附于外部,这三个参数顺序意义是不变的

                //hanoi( n, from, to);              //添加参数后,我们记作 hanoi(3, A, C, B);

                                                                  //因为其对应于对应于move{1,2,3} from A → B,A是起点,所以放在第一个,B是终点,所以放在第三个,C没用到,放中间

                hanoi( n, from, to, mid);       //由外层hanoi()可知,A对应from,B对应mid,C对应to,需要根据外层参数顺序,调整内层参数顺序

                move( n, from, to);               //最小语句,不用改,to确实是终点的含义,如果你把终点记作mid,那这里就要修改了

                //hanoi( n, from, to);               //这里对应于move{1,2,3} from A → C,为迎合参数顺序,对应于hanoi(3. A, B, C),

                hanoi( n, from, mid, to); 

       }

至此,你可以根据自己的需求实现自定义的move( n, from, to),比如可以在里面写一个打印函数,print出"move n from → to",以输出整个移动步骤

还可以返回一个1,略修改代码,你可以输出整个步骤所需的步数,还可以实现某个数+1的操作,同样输出整个移动步数。

注意,你还需要在move中去完成边界判断,n为多少时结束

递归算法三板斧从思路到实现总结

第一板斧

         将第k个问题的解决方法“找”出来,k可以选择4,也可以选择N
         将“第2个”问题的解决方法写出来,找出一个非最小单位的问题,最小单位一步完成,如1,从A到C

第二板斧

         找到一个角度,将第N个问题和第2个问题的解决办法,从形式上统一起来,解决问题的写法统一的时候,完成了50%

第三板斧

         将第N个问题解决办法中的所有一眼看不到底的步骤,统一用一个函数表示,需要注意的是,比如hanoi外层和内层的hanoi参数不一致,需自行调整

         没了。

递归算法三板斧“套用”跨台阶问题

         跳台阶问题:一个台阶总共有n级,如果一次可以跳1级,也可以跳2级         

套用一

        k=N,可以从N-1跳上去,也可以从N-2上跳上去

        k=2,可以从1跳上来,也可以从地面0上跳上来

套用二

        get(N)

                get(N-1)

                get(N-2)

                get(N-1)+get(N-2)

        get(2)

                get(1)

                get(0)

                get(1)+get(0)    注意边界get(1)=get(0)=1

        这个形式很简单,说白了就一加法,前面两步只是为了凑步数写的,只要第三步就行了,这个例子略牵强,读者别介意

 

套用三,注意完成边界判断

         int f( int n){

                  if( n==1|| n==2)
                          return n;
                  return f( n-2)+ f(n-1);
        }

以上就是我对如何实际操作递归算法的理解了,希望对刚学算法的同学或者刚接触算法的准程序员有所帮助,有需要改进的地方,请不吝赐教,每个人看问题的角度都有独到之处,只有多碰撞才能有火花。

当然,没有什么是完全独立的,我们总是站在巨人的肩膀上,非常感谢那么多人无私的将自己的想法表达出来,但这个材料很久了,当时查了多少资料已不记得了。

如果有大神路过,麻烦交流下,告知大神是如何具体操作递归问题的,将不甚感激。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值