常规写法

const Fib1 = n => {
  console.log(`Fib1(${n})`)
  if (n === 0) {
    return 0
  } else if (n === 1) {
    return 1
  } else {
    return Fib1(n - 1) + Fib1(n - 2)
  }
}

console.log(Fib1(5))
// 函数调用顺序
// Fib1(5) Fib1(4) Fib1(3) Fib1(2) Fib1(1) Fib1(0) Fib1(1) Fib1(2) Fib1(1) Fib1(0) Fib1(3) Fib1(2) Fib1(1) Fib1(0) Fib1(1)

clipboard.png

可以发现整个过程是二叉树先序遍历的过程。此外,整个过程中多次调用了Fib1(3)Fib1(2)Fib1(5),产生大量冗余的调用。无路什么数据结构最后都是队栈吗?

一行写出斐波那契数列

const Fib1 = n => (n <= 1) ? n : (Fib1(n - 1) + Fib1(n - 2))

改进写法1

将找到斐波那契数列的第n项问题转化为在一个初始项为t0和t1的加法序列(序列中,每一项都是前两项的和)中找到第n项的问题。

clipboard.png

求以3和7为初始项的第一个序列中的t6(71)可以转化为求以7和10为初始项的第二个序列中的t5(71)

const Fib2 = n => {
  return AdditiveSequence(n, 0, 1)
}

const AdditiveSequence = (n, t0, t1) => {
  console.log(`AdditiveSequence(${n},${t0},${t1})`)
  if (n === 0) {
    return t0
  } else if (n === 1) {
    return t1
  } else {
    return AdditiveSequence(n - 1, t1, t0 + t1)
  }
}

console.log(Fib2(5))
// AdditiveSequence(5,0,1)
// AdditiveSequence(4,1,1)
// AdditiveSequence(3,1,2)
// AdditiveSequence(2,2,3)
// AdditiveSequence(1,3,5)

改进写法2

clipboard.png

求以3和7为初始项的第一个序列中的t6(71)可以转化为求以10和17为初始项的第二个序列中的t4(71)

const Fib3 = n => {
  return AdditiveSequence(n, 0, 1)
}

const AdditiveSequence = (n, t0, t1) => {
  console.log(`AdditiveSequence(${n},${t0},${t1})`)
  if (n === 0) {
    return t0
  } else if (n === 1) {
    return t1
  } else {
    return AdditiveSequence(n - 2, t0 + t1, t0 + t1 + t1)
  }
}

console.log(Fib3(5))
// AdditiveSequence(5,0,1)
// AdditiveSequence(3,1,2)
// AdditiveSequence(1,3,5)

nbb3210
436 声望31 粉丝

优雅地使用JavaScript解决问题