再论递归

再论递归

大概是从汉诺塔百度百科了解递归算法的:

function hanoi(n, a, b, c) {
  if(n===1) {
    console.log(`${a} ---> ${c}`)
    return
  }
  hanoi(n-1, a, c, b);
  hanoi(1, a, b, c);
  hanoi(n-1, b, a, c);
}

hanoi(10, 'A', 'B', 'C');

我自诩脑回路清奇,然而面对这层层递归,大脑CPU却显得不堪重负,甚至一度崩盘!并且作为一个~伪~编程人员,如果让别人知道我对递归这样的基本算法都搞不定,颜面何存呐?!!所以,对此我一直耿耿于怀。

直到昨天晚上(2017/12/21真是个伟大的日子),我在知乎看到一个回答,简单粗暴。原答案见python中的汉诺塔递归算法的具体运算过程是怎样的?

@半岛 的回答:

重点其实是:不要一开始就关心每一步怎么解决的,你只需要把函数当成一个实现你目的的神器,随时调用。也就是递归。

1

比如说我们有一个万能神器move,只需要给它几个参数,即可自动完成一个功能:把n个盘子利用缓冲区,从起点运送到终点,期间严格遵守汉诺塔规则。
这里你暂时不需要去了解每一个步是如何实现的。

move(N,起点,缓冲区,终点)
N: 盘子的个数。

2

现在有个n个盘子,a,b,c三个塔。
把n个盘子抽象成两个盘子,n-1 和 底下最大的1

n = (n-1) + 1

这个最简单的玩法如何实现呢?

  • 首先:把n-1 移到 缓冲区 -------过程1
  • 然后:把1 移到 终点 -------过程2
  • 最后:把缓冲区的n-1 移到 终点 -------过程3

3

过程1 如何实现?
还是召唤神器吧。

move(N,起点,缓冲区,终点)
此时,我们的起点是a,终点是b ,N=n-1,缓冲区只能是c了
move(n-1,a,c,b)
过程2呢?
move(1,a,b,c)
过程3呢?
move(n-1,b,a,c)

4

哦哦 神器的力量太大,止不住咋办。。

if (N == 1):
a -> c #此时我已经不需要缓冲区了

过程1-3是整个递归算法的核心。再抽象一点,就是广为人知的大象装冰箱三部曲:

  • 打开冰箱门
  • 把大象装进冰箱
  • 关上冰箱门

oh my my,胜利的号角已经吹响,我们要把五星红旗插在敌人固守的高地上!

实现递归的基本步骤

  • 1、实现一个功能需要 n 次递归调用;

  • 2、假设前面 n-1 次已经执行完毕;

  • 3、执行最后一步操作

  • 4、添加递归边界条件

判别递归使用场景

最后,什么时候需要用到递归呢?一个简单的判别方法是:当前的输入依赖上一步的输出,依次重复。

最后的最后,做一个小练习巩固一下印象吧:

/* 
小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币,但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。
魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币
魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币
小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币,小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。 
输入描述:
输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。

输出描述:
输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。

输入例子1:
>10

输出例子1:
>12

*/

// TODO:




// js实现

function createMagic(n) {
  let arr = [];

  function magicCoin(n) {

    if (n === 1) return arr.push(1);
    if (n === 2) return arr.push(2);

    if (n % 2 === 0) {
      magicCoin((n - 2) / 2);
      arr.push(2);
    } else {
      magicCoin((n - 1) / 2);
      arr.push(1);
    }
  }
  magicCoin(n);

  return arr.join('')
}

// test

for(var i = 1; i<20; i++) {
  console.log(i+': ' +createMagic(i));
}

敬请指正!

相关推荐