【问题标题】:What is the time complexity of this for loop nested in a while loop?嵌套在 while 循环中的这个 for 循环的时间复杂度是多少?
【发布时间】:2017-09-02 05:22:39
【问题描述】:

我正在尝试优化一个功能。我相信这个嵌套的 for 循环是二次的,但我并不肯定。我已经重新创建了下面的函数

const bucket = [["e","f"],[],["j"],[],["p","q"]]
let totalLettersIWantBack = 4;

//I'm starting at the end of the bucket
function produceLetterArray(bucket, limit){
  let result = [];
  let countOfLettersAccumulated = 0;
  let i = bucket.length - 1;
    while(i > 0){
      if(bucket[i].length > 0){
        bucket[i].forEach( (letter) =>{
        if(countOfLettersAccumulated === totalLettersIWantBack){
          return;
        }
        result.push(letter);
       countOfLettersAccumulated++;
        })
      }
      i--;
    }
  return result;
}

console.log(produceLetterArray(bucket, totalLettersIWantBack));

【问题讨论】:

  • 如果我们无论如何都不能运行它,你为什么要把它做成一个 sn-p?
  • @Vallentin 对此感到抱歉。现在可以执行了。

标签: javascript time-complexity big-o computer-science


【解决方案1】:

这在技术上是线性的,n 是矩阵中元素的总数。这是因为退出条件是 bucket 的长度,并且对于 bucket 中的每个数组,您检查 countOfLettersAccumulated 是否等于 totalLettersIWantBack。持续关注价值观。

如果您正在寻找与矩阵尺寸匹配的答案,情况会变得更加复杂,因为看起来 bucket 的尺寸并不固定。

您可以通过在 bucket foreach 之外添加一个额外的检查来将这段代码变为常量,如果 countOfLettersAccumulated 等于 totalLettersIWantBack 然后你休息一下。

【讨论】:

    【解决方案2】:

    这是针对此类问题的技巧。对于您要分析其复杂性的代码,只需写下在不存在其他语句的最坏情况下执行每个语句所需的时间。注意以#operations worst case:开头的cmets

    对于给定的代码:

    while(i > 0){ //#operations worst case: bucket.length
      if(bucket[i].length > 0){ //#operations worst case:: 1
        bucket[i].forEach( (letter) =>{  //#operations worst case: max(len(bucket[i])) for all i
        if(countOfLettersAccumulated === totalLettersIWantBack){ //#operations worst case:1
          return;
        }
        result.push(letter); //#operations worst case:1
       countOfLettersAccumulated++; //#operations worst case:1
        })
      }
      i--; ////#operations worst case:: 1
    }
    

    我们现在可以将所有最坏情况时间相乘(因为它们都可以在最坏情况下实现,所以您始终可以设置 totalLettersIWantBack = 10^9)以获得 O 复杂度sn-p:

    复杂度 = O(bucket.length * 1 * max(len(bucket[i])) * 1 * 1 * 1 * 1)

    = O(bucket.length * max(len(bucket[i]))

    如果每个 bucket[i] 的长度是一个常数 K,那么您的复杂性会降低到: O(K * bucket.length ) = O(bucket.length)

    请注意,随着元素数量的增加,推送操作的复杂性可能不会保持不变(最终,运行时将需要为添加的元素分配空间,并且可能必须移动所有现有元素)。

    【讨论】:

    • 感谢您的详细解答。所以你说最好的情况是 O(n)?
    • @colbisaurusrex,当然。是的,如果每个子数组中的元素数量是一个常数,那么最坏的情况确实是 O(N),其中 N = 外部数组(桶)中的元素数量。另外,请注意,Big-O(或最坏情况下的时间复杂度)实际上告诉您代码所花费的时间如何随着输入的变化而变化。例如,冒泡排序是 O(n^2)。如果 4 个元素需要 4 秒,则 8 个元素需要 16 秒。您可以将其视为对每个算法或函数的排名。
    【解决方案3】:

    这是否是二次的取决于您考虑的 N 以及存储桶的组织方式。如果 N 是字母的总数,则运行时间受存储桶中的 bin 数量(如果大于 N)或受存储桶中的字母数量(如果 N 更大)的限制。在任何一种情况下,搜索时间都会随着更大的界限线性增加,如果一个会主导另一个,那么时间复杂度是 O(N)。这实际上是一个带有“转弯”的线性搜索,对线性搜索进行处理并将其间隔开并不会改变时间复杂度。一段代码中存在多个循环并不仅仅使它成为非线性的。再次以线性搜索为例。我们搜索一个列表,直到找到最大的元素。

    //12 elements
    var array = [0,1,2,3,4,5,6,7,8,9,10,11];
    var rows = 3;
    var cols = 4;
    
    var largest = -1;
    
    for(var i = 0; i < rows; ++i){
    
        for(var j = 0; j < cols; ++j){
            var checked = array[(i * cols) + j];
            if (checked > largest){
                largest = checked;
            }      
        } 
    }
    console.log("found largest number (eleven): " + largest.toString()); 

    尽管使用了两个循环而不是一个循环,但运行时复杂度仍然是 O(N),其中 N 是输入中的元素数。将其缩小,因此每个索引实际上是一个包含多个元素的数组,或者用空箱分隔相关元素并不会改变运行时复杂度线性绑定的事实。

    【讨论】:

      【解决方案4】:

      我喜欢@axiomexplanation 的复杂性分析。

      只是想添加可能的优化解决方案。

      UPD .push (O(1)) 比 .concat (O(n^2)) 快

      这里也是测试Array push vs. concat

      const bucket = [["e","f"],[],["j", 'm', 'b'],[],["p","q"]]
      let totalLettersIWantBack = 4;
      
      //I'm starting at the end of the bucket
      function produceLetterArray(bucket, limit){
        let result = [];
      
        for(let i = bucket.length-1; i > 0 && result.length < totalLettersIWantBack; i--){
          //previous version
          //result = result.concat(bucket[i].slice(0, totalLettersIWantBack-result.length));
          
          //faster version of merging array
          Array.prototype.push.apply(result, bucket[i].slice(0, totalLettersIWantBack-result.length));
        }
        return result;
      }
      
      console.log(produceLetterArray(bucket, totalLettersIWantBack));

      【讨论】:

      • 感谢您的回答。 concat 是否使时间复杂度为 O(n^2)?
      • 是的,伙计,你是对的。我不知道 =) 我已经更新了答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-27
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多