堆栈与斐波那契

75 阅读3分钟

什么是堆栈

  • 堆栈是一种特殊的线性表,它遵循后进先出(LIFO)原则,即后进入的元素先出。
  • 就像是洗盘子,洗的放在最上,用的先拿最上的。

什么是斐波那契数列

  • 斐波那契数列的前两个数是0和1,之后每个数都是前两个数的和。
  • 除了f(0)=0, f(1)=1,其他数都是f(n)=f(n-1)+f(n-2)。
  • 0~10的斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

代码实现斐波那契

function fib(n) {
    if (n <= 1) {
        return n;
    } else {
        return fib(n - 1) + fib(n - 2)
    }
}

为什么使用递归实现斐波那契

  • 通常情况下,直接使用堆栈来实现斐波那契数列并不是最优选择,因为斐波那契数列的计算更适用于递归算法。
  • 通过使用堆栈来模拟递归过程,同时计算大n时避免了传统递归可能导致的栈溢出问题。

堆栈实现斐波那契

function fib(n) {
    // 输入校验
    if (n < 0) throw new Error("输入正确的长度!!");

    // 初始化栈,压入一个对象,包含三个关键值:当前要计算斐波那契数的位置n,以及当前位置的前两个数a和b(初始时a=f(0)=0,b=f(1)=1)。
    const stack = [{n, a: 0, b: 1}];
    while (stack.length > 0) {
        const {n: currentN, a, b} = stack.pop();
        // 如果当前要计算斐波那契数的位置n为0,表示计算以及计算到基础情况(F(0)=0)则返回a表示计算结果。
        if (currentN === 0) {
            return a;
        } else {
            // 否则,说明还需要继续计算,将当前位置的前两个数a(f(n-2))和b(f(n-1))依次压入栈中(请看下方详细解释)。
            stack.push({n: currentN - 2, a, b});
            stack.push({n: currentN - 1, a: b, b: a + b});
        }
    }
}

为什么先压入(n-2)再压入(n-1)

  • 为了保证计算顺序,按照斐波那契数列的定义,f(n)=f(n-1)+f(n-2),先压入f(n-2)因为他不依赖f(n),因为有初始值a和b,再压入f(n-1)(此时f(n-1)=b,f(n)=a+b)。
  • 再每一次计算时,位置n再逐渐减小,而a替换为b,b再替换为a+b,逐渐累加的过程。
  • 所以当位置起始为5时,
    • 压入(n-2)此时n=3,a=0,b=1
    • 压入(n-1)此时n=4,a=0,b=0+1
    • 取出(n-1)此时n=4,a=0,b=1
    • 压入(n-2)此时n=2,a=0,b=1
    • 压入(n-1)此时n=3,a=1,b=0+1
    • 取出(n-1)此时n=3,a=1,b=1
    • 压入(n-2)此时n=1,a=1,b=1
    • 压入(n-1)此时n=2,a=1,b=1+1
    • 取出(n-1)此时n=2,a=1,b=2
    • 压入(n-2)此时n=0,a=1,b=2
    • 压入(n-1)此时n=1,a=2,b=1+2
    • 取出(n-1)此时n=1,a=2,b=3
    • 压入(n-2)此时n=-1,a=2,b=3
    • 压入(n-1)此时n=0,a=3,b=2+3
    • 取出(n-1)此时n=0,a=3,b=5
    • 因为n==0,所以返回a=3,即f(5)=3。
  • 如图所示
    • image.png
恐有疏漏,如有发现,敬请指正,十分感谢!