【问题标题】:Number of ways to get from top left corner to bottom right corner in MxN grid while moving only down or right. What is time and space complexity?仅向下或向右移动时从 MxN 网格的左上角到右下角的方法数。什么是时间和空间复杂度?
【发布时间】:2021-11-11 19:22:37
【问题描述】:

问题:在 MxN 网格中从左上角到右下角找到多种可能的方式,而你只能向下或向右移动。

这是我编写的两种算法。结果看起来不错,但我无法计算出时间和空间的复杂性,我对可能的复杂性有一些猜测,但我无法以“正确”的方式证明它们。


朴素算法:

    function gridTravel(m, n) {
      if(m<1 || n<1) return 0;
      if (m === 1 || n === 1) return 1;
      return gridTravel(m-1, n) + gridTravel(m, n-1);
    };
    console.log(gridTravel(10,10));

我的猜测

  • 空间复杂度 - O(n+m)?可能的最长调用堆栈似乎是“线性”扩展的,因此假设一些近似值将是 O(n+m),但我无法真正证明或反驳它。
  • 时间复杂度是指数级的,因为每个位置可以创建 2 个新位置 - O(2^n) 或 O(2^n+m),不确定哪个更合适。
                   m:n
        m-1:n                m:n-1
    m-2:n  m-1:n-1     m-1:n-1   m:n-2

但同样,我对这个解释没有信心,因为它不是对称树,它只是一开始的样子。


朴素算法 + 记忆:


    seenGrids = {};
    const gridTravel = (m, n) => {
      if(m<1 || n<1) return 0;
      if (m === 1 || n === 1) return 1;
      if (`${m}:${n}` in seenGrids || `${n}:${m}` in seenGrids) {
        return seenGrids[`${m}:${n}`] || seenGrids[`${n}:${m}`];
      }
      seenGrids[`${m}:${n}`] = gridTravel(m-1, n) + gridTravel(m, n-1);
      return seenGrids[`${m}:${n}`];
    };

我的猜测:

  • 空间 - O(n*m)?调用堆栈似乎仍然是线性的,但现在我们有了这个不断增长的对象seenGrids,根据我的直觉,它应该以二次方式缩放吗?我不知道如何证明或反驳它,当我为200x200 网格运行console.log(Object.keys(seenGrids).length) 时,我得到19900,它既不是m*n 也不是m+n,所以它是线性的还是二次的?
  • 时间 - O(n*m)? - 这对我来说是最难缠的。它不再是指数级的,因为由于保存了答案,很多子树都被跳过了,但我不知道如何以“正确”的方式推导时间复杂度。

【问题讨论】:

    标签: javascript algorithm time-complexity big-o space-complexity


    【解决方案1】:

    第一个算法:

    对于时间复杂度,最简单的证明就是你的方法!我的意思是递归函数的计算树。可以看到,展开有一个二叉树,树的最大长度为min(n,m)。因此,时间复杂度在O(2^(min(m,n)))

    对于空间复杂度,正如您所指出的,堆栈的计算将以深度优先的方式完成。因此,由于每个节点的最大分支为 2,树的最大长度为min(m,n)(如上一段所述),空间复杂度将为2 min(m,n)

    第二种算法

    由于输入的最大不同组合为m * n(函数的第一个和第二个输入分别为[1, 2, ..., m]和[1, 2, ..., n] ),堆栈调用的最坏情况在O(min(m, n)),空间复杂度在O(m * n),也就是内存数组的大小。

    对于时间复杂度,我们可以确保数组的每个元素都会被计算一次(因为它是被记忆的)。因此,与递归回调的复杂度无关,并且由于递归的深度优先计算(自下而上),第二种算法的时间复杂度为O(m * n)

    【讨论】:

    • 您确定堆栈的大小在 navie 算法中是指数级的吗?我想象它的方式,它沿着树的左分支,一旦它到达叶子,叶子立即返回值,所以它从堆栈中弹出,然后回到那个叶子的父节点并转到右子节点并重复.所以从我的观点来看,我们实际上并没有创建一个调用堆栈来保存所有可能的调用,因为它会在途中弹出很多。叶子是指不分支到其他调用而是立即返回值的“节点”
    • @hdw3 你绝对是对的。我犯了一个错误!它已更新。
    猜你喜欢
    • 1970-01-01
    • 2015-02-17
    • 2013-12-22
    • 1970-01-01
    • 2019-09-17
    • 2021-08-21
    • 2020-09-05
    • 2020-09-01
    • 2016-06-08
    相关资源
    最近更新 更多