Javascript array.prototype.includes与set.prototype.has的时间复杂度

Javascript array.prototype.includes与set.prototype.has的时间复杂度,javascript,arrays,performance,set,time-complexity,Javascript,Arrays,Performance,Set,Time Complexity,我一直在阅读关于现代javascript引擎在javascript中的集合与数组的时间复杂性的相互矛盾的答案 我完成了Codibility的演示任务,这是一个简单的赋值,用于找到以下问题的解决方案:给定一个由N个整数组成的数组a,返回a中不出现的最小正整数(大于0) 例如,给定A=[1,3,6,4,1,2],函数应该返回5 我的第一个解决方案是: const solution = arr => { for(let int = 1;;int++) { if (!arr

我一直在阅读关于现代javascript引擎在javascript中的集合与数组的时间复杂性的相互矛盾的答案

我完成了Codibility的演示任务,这是一个简单的赋值,用于找到以下问题的解决方案:给定一个由N个整数组成的数组a,返回a中不出现的最小正整数(大于0)

例如,给定A=[1,3,6,4,1,2],函数应该返回5

我的第一个解决方案是:

const solution = arr => {
    for(let int = 1;;int++) {
        if (!arr.includes(int)) {
            return int;
        }
    }
}
现在,奇怪的是Codibility说这个解决方案的时间复杂度为O(n**2)(他们更喜欢复杂度为O(n)的解决方案。据我所知,array.prototype.includes是一个线性搜索(),意味着它应该有O(n)的时间复杂度

如果我使用集合输入不同的解决方案,我会得到满分:

const solution = arr => {
  const set = new Set(arr);
  let i = 1;

  while (set.has(i)) {
    i++;
  }

  return i;
}
Codibility说这显然具有O(N)或O(N*log(N))的时间复杂度

这是否正确?array.prototype.includes实际上是O(n**2)而不是O(n)

最后,我有点不明白为什么Set.has()是首选的,因为在我的控制台性能测试中,Array.includes()始终优于先创建一个集合然后在集合上查找它的解决方案,如下面的代码段所示

const rand=(size)=>[…数组(size)].map(()=>Math.floor(Math.random()*size));
常数小=兰特(100);
常数中等=兰特(5000);
常量大=兰特(100000);
常数解决方案1=arr=>{
console.time('Array.includes');
for(设int=1;;int++){
如果(!arr.includes(int)){
console.timeEnd('Array.includes');
返回int;
}
}
}
常数解2=arr=>{
console.time('Set.has');
常数集=新集(arr);
设i=1;
while(set.has(i)){
i++;
}
console.timeEnd('Set.has');
返回i;
}
log('测试小数组:');
溶液1(小);
溶液2(小);
log('Testing medium array:');
溶液1(培养基);
溶液2(培养基);
log('测试大型数组:');
溶液1(大);

解决方案2(大);
这样的比较并不完全公平,因为在使用集合的函数中,需要先将数组转换为集合,这需要一些时间

如果忽略此项,请查看以下结果。为了直接比较,我已更新了
solution2
函数以接收
Set
,并将
while
循环更改为
for
循环

您可能会注意到,对于一个小数组,Set可能会慢一些。这并不重要,因为时间复杂度只会影响一个大的(重要的)
n

另请注意,
Array.includes
确实是O(n),但因为它位于
for
循环中,在最坏的情况下可能上升到
n
解决方案的时间复杂度为O(n^2)

const rand=(size)=>[…数组(size)].map(()=>Math.floor(Math.random()*size));
常数小=兰特(100);
常数中等=兰特(5000);
常量大=兰特(100000);
常数解决方案1=arr=>{
console.time('Array.includes');
for(设int=1;;int++){
如果(!arr.includes(int)){
console.timeEnd('Array.includes');
返回int;
}
}
}
常数solution2=集合=>{
console.time('Set.has');
for(设i=1;;i++){
如果(!set.has(i)){
console.timeEnd('Set.has');
返回i
}
}
}
log('测试小数组:');
溶液1(小);
解决方案2(新设置(小));
log('Testing medium array:');
溶液1(培养基);
溶液2(新组(介质));
log('测试大型数组:');
溶液1(大);

解决方案2(新集合(大);
包含的
是线性的,但是你的
for
循环的外部
也可以上升到
n
。使用
Set
方法,
新集合(arr)
是O(n),但是
Set.has(i)
是O(1),这意味着O(2n)总计(
循环被考虑在内,最坏的情况
n
),减少到O(n)(如果我没有错的话)。这是有道理的,谢谢!