【问题标题】:Preventing a loop within a loop. How to reduce complexity防止循环中的循环。如何降低复杂性
【发布时间】:2015-08-17 20:52:43
【问题描述】:

您有 2 个数组。
arrA 为空。
arrB 充满了东西(它充满了什么并不重要,但假设它很大)。

当用户做某事时,会从 arrB 中删除一个项目,并将该项目放在 arrA 中。

当用户做不同的事情时,它会从 arrA 中提取项目并将其放入 arrB。

是否可以在循环中不包含循环的情况下执行此操作?

或者用计算机科学术语来说:
是否可以使用线性 ( ϴ(n) ) 时间/空间复杂度来做到这一点?
现在我有一些至少是 ϴ(n*k)

(其中n是arrB的长度,k是传递给applyItems的项目数):

var arrA = [], arrB = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];

function addToArray(arrayToAddTo, item){
  if(arrayToAddTo.indexOf(item) === -1){
    arrayToAddTo.push(item);
  }
}

function removeFromArray(arrayToRemoveFrom, item){
  var i = arrayToRemoveFrom.length;
  var temp = [];
  while(i--){
    if(arrayToRemoveFrom[i] !== item){
      temp.push(arrayToRemoveFrom[i]);
    }   
  }
  return temp;
}

function applyItems(arrayOfItems){
  var i = arrayOfItems.length;
  while(i--){
    var current = arrayOfItems[i]
    addToArray(arrA, current);
    arrB = removeFromArray(arrB, current);
  }
}

applyItems([0, 5, 3]);
console.log(arrA);
console.log(arrB);

applyItems 有效,但效率不高。

这里是否可以降低时间/空间复杂度?

【问题讨论】:

  • 您可以使用比手动循环更快的本地工具。 removeFromArray 可以使用indexOf 获取要移除的位置,然后使用splice 将其移除。您也可以使用参考而不是每次都重新创建数组。
  • 类似这样的东西?-http://jsfiddle.net/p6bnLdoq/,修改了removeFromArray方法以避免循环并使用拼接方法
  • 我不明白你目前的方法是O(n²)。它是O(n*k),其中narrB 中的项目数,karrayOfItems 中的项目数。这种复杂性完全没问题。
  • 您可能希望在 addToArray 中省略 indexOf。似乎已经确定 arrAarrB 中都没有项目(并且您的算法保持该不变性),因此您不必在将项目从一个交换到另一个之前检查重复项
  • @AndrewLuhring: 是的,您可以使用arrayOfItems 的有效查找结构使其成为O(n+k),该结构不需要循环,但允许您在O(1) 中确定一个项目是否应该是交换了。这样,通过arrB 一次就足够了。但是,如果数组是有限的(并且非常小),那几乎没有关系

标签: javascript arrays performance time-complexity space-complexity


【解决方案1】:

根据我的评论:

您可以使用比手动循环更快的本地工具。 removeFromArray 可以使用 indexOf 获取要删除的位置,然后使用 splice 将其删除。您也可以使用参考而不是每次都重新创建数组。

还有一些其他的优化...

var arrA = [], arrB = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];

function addToArray(item){
  if(arrA.indexOf(item) === -1){
    arrA.push(item);
  }
}

function removeFromArray(item){
  var index = arrB.indexOf(item);
  if (index > -1) {
    arrB.splice(index, 1);
  }
}

function applyItems(arrayOfItems){
  arrayOfItems.map(function(item) {
      addToArray(item);
      removeFromArray(item);
  });
}

applyItems([0, 5, 3]);
console.log(arrA);
console.log(arrB);

【讨论】:

  • 为什么你认为“原生工具”“比手动循环快得多”?而且它们仍然具有相同的复杂性。
  • 与@Bergi 相同的点 - 通过循环遍历数组中的项目,拼接不起作用吗?另外, indexOf 不也循环遍历数组中的项目吗?仅仅因为它是一个内置函数并不意味着它是有效的。
  • 我发现@Bergi 提到的想法的方式是因为我有一次从python 翻译了一些代码。该函数使用了python的“sum”函数(它通过循环遍历数组来将数组中的所有值相加。我认为。)。据我所知,javascript 中没有等价物,但即使有,在循环中使用它也不会有效,因为您已经在循环中循环。
  • javascript 是一种解释型语言这一事实​​使得它在定义上比编译型语言慢。所以总是 更快使用本机javascript函数,因为它们是在引擎中编译的。但是,嘿,给自己一个机会,试着打败它。 jsperf.com/array-remove-loop-vs-native
  • @AndrewLuhring:即使在 python 中,尝试手动求和元素或使用sum,您会看到很多的差异。这不是复杂性的问题,而是代码执行环境的问题。一个是由计算机解释的一系列字节码,另一个在执行之前有一层解释器和编译器。
【解决方案2】:

现在我有一些至少是 ϴ(n*k)

您可以通过使用arrayOfItems 的高效查找结构使其成为O(n+k),该结构不需要循环,但允许您在O(1) 中确定是否应将项目交换到另一个数组中。这样,通过arrB 一次就足够了。
或者,如果您对数组进行排序并使用二进制搜索进行查找,您将拥有O(log k * (n+k))。但是,如果您的数组是有限的并且非常小,那几乎没有关系。

您还应该省略addToArray 中的indexOf 测试。似乎已经确定 arrAarrB 中都没有项目(并且您的算法保持该不变性),因此您不必在将项目推送到数组之前检查重复项。

【讨论】:

    【解决方案3】:

    是的。您可以使用splice(),而不是制作自己的删除功能。

    因此,您可以使用arrB.splice(i, 1); 而不是removeFromArray(arrB, current)。这将从索引i 中删除1 元素。

    您不需要遍历每个元素来检查它是否与您想要的匹配 - 只需使用 indexOf()。所以你可以做类似的事情

     var i = arrA.indexOf(item)
    

    【讨论】:

    • 不能通过循环遍历数组中的项目来进行拼接吗?另外,indexOf 不也循环遍历数组中的项目吗?
    • 是的,我相信两者都是 O(n)。我将您的问题解释为无需循环即可做到这一点。
    • 我的错。应该澄清。这更像是一个算法问题,而不是特定于 JavaScript 的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-26
    相关资源
    最近更新 更多