简体   繁体   English

在 JavaScript 中使用 filter() 在两个未排序数组中查找交集的大 O

[英]Big O of Finding the Intersection in two Unsorted Arrays Using filter() in JavaScript

I just started learning Big O notation and I'm trying to understand the Big O of different functions to see which one is better.我刚开始学习大 O 符号,我正在尝试了解不同功能的大 O,看看哪个更好。

I'm struggling to calculate the time and space complexity for the following code.我正在努力计算以下代码的时间和空间复杂度

function findCommonElem(arr1, arr2) { 
  let result = arr1.filter(x => arr2.includes(x));
  console.log(result);
}

  findCommonElem(arr1, arr2);

From what I understand, common array methods like filter() usually have a big O of O(n) so in this case, it'd be O(m+n) depending on the length of each array.据我了解,像filter()这样的常见数组方法通常有O(n)的大 O,所以在这种情况下,它会是O(m+n)具体取决于每个数组的长度。 However, I could be super wrong.但是,我可能是超级错误的。

Can someone explain, please?有人可以解释一下吗? Thanks so much!非常感谢!

Bonus question: Compared to sorting the arrays then using a while loop for the same function, which one would be considered as "better"?额外问题:与对数组进行排序然后对同一函数使用 while 循环相比,哪一个被认为“更好”?

The above function has time complexity of O(M * N) .上述函数的时间复杂度为O(M * N)

But, can you make this solution more efficient?但是,你能让这个解决方案更有效率吗? Yes.是的。 You can reduce it to O(M + N) .您可以将其减少到O(M + N)

TLDR - Use a hash table to achieve linear time complexity O(M + N) TLDR - 使用哈希表实现线性时间复杂度O(M + N)

Let's see.让我们来看看。


Approach 1方法一

Check every elements of array 1 with every element of array 2. (This is the approach you're using.)用数组 2 的每个元素检查数组 1 的每个元素。(这是您正在使用的方法。)

 function findCommonElem(arr1, arr2) { return arr1.filter(x => arr2.includes(x)); } const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20]; console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];

  • Time complexity = O(M * N)时间复杂度 = O(M * N)

    • Every element from array 1 is checked with every element of array 2 at the worst case.在最坏的情况下,使用数组 2 的每个元素检查数组 1 中的每个元素。 So, it's M * N.所以,它是 M * N。
  • Space compexity = O(M) or O(N)空间复杂度 = O(M)O(N)

    • At most, all of the elements from one of the either arrays could be in the intersection.至多,来自任一阵列之一的所有元素都可以在交集中。

Approach 2方法二

Use a hash map to linearize the nested loop.使用哈希映射将嵌套循环线性化。 First, populate the hash map with array 1 elements.首先,使用数组 1 元素填充哈希映射。 Then check through the array 2 using the map to find the intersection.然后使用地图检查数组 2 以找到交点。

 function findCommonElem(arr1, arr2) { const map = new Map(); arr1.forEach(item => map.set(item, true)); return arr2.filter(item => map.has(item)); } const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20]; console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];

The above function returns the same output.上述函数返回相同的输出。 But here's the difference - the nested loop is reduced to two linear loops for the arrays.但不同之处在于 -嵌套循环减少为数组的两个线性循环。 This means both arrays are traversed only once.这意味着两个数组只被遍历一次。

  • Time complexity = O(M + N)时间复杂度 = O(M + N)

    • Array 1 is traversed once (M elements).数组 1 被遍历一次(M 个元素)。
    • Array 2 is traversed once (N elements).数组 2 被遍历一次(N 个元素)。
    • Checking if the map contains the element with map.has() takes constant time O(1) .检查地图是否包含带有map.has()的元素需要恒定时间O(1)
    • Total run time = M + N总运行时间 = M + N
  • Space compexity = O(M) or O(N)空间复杂度 = O(M)O(N)

    • Space complexity is still the same here, because space needed for the new hash map is either O(M) or O(N) .空间复杂度在这里仍然相同,因为新哈希映射所需的空间是O(M)O(N) And our intermediate array takes either O(M) or O(N) .我们的中间数组采用O(M)O(N) Which is still the same.这仍然是一样的。

Bonus: Don't know much about how hash maps work internally?奖励:不太了解哈希映射在内部是如何工作的? Watch this .这个

Approach 3方法三

Use set instead of map .使用set而不是map The set data structure is best for this use case as you don't need the mapped value (the true value) in approach 2.集合数据结构最适合此用例,因为您不需要方法 2 中的映射值( true值)。

 function findCommonElem(arr1, arr2) { const set = new Set(arr1); return arr2.filter(item => set.has(item)); } const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const arr2 = [2, 4, 6, 8, 10, 12, 14, 16, 20]; console.log(findCommonElem(arr1, arr2)); // [2, 4, 6, 8, 10];

This uses relatively lesser space but the algorithmic complexity of TC and SC are still same.这使用了相对较少的空间,但 TC 和 SC 的算法复杂度仍然相同。

  • Time complexity = O(M + N)时间复杂度 = O(M + N)
  • Space compexity = O(M) or O(N)空间复杂度 = O(M)O(N)

Thanks to Nick Parsons for pointing this out.感谢尼克帕森斯指出这一点。

let's say that arr1.length is n and arr2.length is m.假设arr1.length是 n 和arr2.length是 m。 so the filter function run the lambda function you wrote for each item in arr1 .所以filter函数运行你为arr1每个项目编写的 lambda 函数。 The lambda function checks if an item is in the arr2 and in the worst case where it didn't find it the function ran on all the array so m times. lambda 函数检查一个项目是否在arr2 ,在最坏的情况下,它没有找到它,该函数在所有数组上运行了 m 次。 therefore arr1.filter(x => arr2.includes(x)) run at the worst case O(n*m).因此arr1.filter(x => arr2.includes(x))在最坏的情况下运行 O(n*m)。

as for the space complexity, the filter function creates a new array and in the worst case that array size is as big as the original so the space complexity is O(n)至于空间复杂度, filter函数会创建一个新数组,在最坏的情况下,数组大小与原始数组一样大,因此空间复杂度为 O(n)

As it correctly mentioned, big O here would be equal to O(n*m) , here is an explanation for this:正如它正确提到的,这里的大 O 将等于O(n*m) ,这是对此的解释:

  1. arr1.filter complexity is O(n) arr1.filter复杂度为 O(n)
  2. arr2.includes complexity is O(n) as well arr2.includes复杂度也是 O(n)

However for each iteration in .filter you execute each time arr2.includes, which leads to O(n) * O(n) = O(n * m)但是,对于 .filter 中的每次迭代,您每次都会执行 arr2.includes,这导致O(n) * O(n) = O(n * m)

You may increase performance if you replace for example arr2 with JS Set.如果将 arr2 替换为 JS Set,则可能会提高性能。 JS Set.has complexity is O(1) , so using Set instead of arr2 should help you to achieve O(n) complexity. JS Set. 的复杂度是O(1) ,因此使用 Set 而不是 arr2 应该可以帮助您实现O(n)复杂度。

I believe I cannot answer on sorting question, cause I haven't understood clearly what you mean.我相信我无法回答排序问题,因为我还没有清楚地理解你的意思。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM