【问题标题】:Fastest way to get ONLY unique values from array?从数组中获取唯一值的最快方法?
【发布时间】:2018-03-14 03:46:39
【问题描述】:

我有一个这样的数组

students = [{name: 'Abbey', age: 25}, {name: 'Brian', age: 45},
            {name: 'Colin', age: 25}, {name: 'Dan', age: 78}]

我希望输出是;

uniqueAges = [45, 78]

需要明确的是,如果学生数组中出现多次的年龄值,我不希望在我的 uniqueAges 数组中任何具有该年龄的对象。 “Abbey”和“Colin”的年龄相同,所以他们出局了。

我知道我可以这样做并运行uniqueAgeGetter(students)

   function uniqueAgeGetter(list){
   var listCopy = list.slice();
   var uniqueAges = list.slice();
   for (var i = list.length - 1; i >= 0; i--) {
        for (var j = listCopy.length - 1; j >= 0; j--) {
            if(listCopy[j].name !== list[i].name && 
                listCopy[j].age == list[i].age){
                  uniqueAges.splice(i, 1)
                }   
            }
    }
   console.log(uniqueAges)
   return uniqueAges
 }

但是是否可以在没有第二个循环的情况下做到这一点?我不是时间复杂度方面的专家,但我试图找出这个任务是否可能是 O(n)。

编辑: 我不是在问uniqueAgeGetter 是否被重写以更好地阅读或使用诸如 map、reduce 或 filter 之类的函数(因为我的理解是它们最终也是一个循环)。

我的问题是可以以降低时间复杂度的方式重构 uniqueAgeGetter 吗?可以只用一个循环完成吗?

谢谢。

【问题讨论】:

  • 尝试使用设置
  • @oybek Set 很棒,但尚未得到广泛支持
  • 如前所述,O(n) 的复杂性是完全可能的。但是,在一次迭代中 不可能在所有元素上获得格式良好的 array [45, 78] 的输出。 (这里的所有解决方案都需要对多个元素进行一次以上的迭代)但是,在仅对元素进行一次迭代的同时在 Set 中获得所需的输出。那是你感兴趣的东西吗?
  • @Victor 绝大多数浏览器都支持 Sets。它们是 ES2015 的一个特性,在大多数情况下,只有 非常 没有更新的过时浏览器不理解 Sets。

标签: javascript node.js algorithm time-complexity


【解决方案1】:

这可以在O(n) 时间内完成,方法是计算一个年龄被看到的次数,并过滤掉计数超过一的年龄。

由于年龄有合理的限制,我们可以使用长度等于最大可能年龄的整数数组来存储年龄计数。在下面的示例中,我将可能的最大年龄设为舒适的200

var students = [
  {name: 'Abbey', age: 25 }, 
  {name: 'Brian', age: 45 },
  {name: 'Colin', age: 25 }, 
  {name: 'Dan', age: 78 }
];

var studentAges = students.map(val => val.age);
var ageCounts = Array(200).fill(0);

studentAges.forEach(age => ageCounts[age] += 1);

var uniqueAges = studentAges.filter(age => ageCounts[age] == 1);

console.log(uniqueAges);

【讨论】:

  • 使用对象或 Map 并仅初始化出现在数组中的值可能会更快,并且如果不满足假设(例如年龄是 0 到 200 之间的整数)不会失败.
  • 关于不需要假设的有效观点,@Bergi。但是,我不认为使用地图一定会更快,尤其是对于较大的输入。散列和重新散列以及解决散列冲突所花费的时间应该超过任何性能优势(我看不到任何 - 也许我错过了什么?)
【解决方案2】:
  • 第一个想法,我们可以做两步:

    Step1:对数组进行排序

    -- 有很多算法可以做到这一点。据我所知,目前最佳算法的复杂度是 O(Nlog(N)),其中 N 是数组的数量。

    Step2:删除重复元素

    -- 这一步的复杂度是O(N) 因此,经过两步,复杂度为 O(N) + O(Nlog(N))。最后,复杂度是O(Nlog(N))

  • 第二个想法

    这也有复杂性是 O(Nlog(N)) 但下次你想获得唯一年龄时它会是 O(N)。

    除了将数据保存在数组中之外,您还可以通过一些自定义在二叉搜索树中重建。这棵树中的这个节点将保存所有具有相同年龄的元素。

    第一次构建树的复杂度是 O(Nlog(N))

关于复杂度为O(N)的算法,目前我认为没有技术可以解决它。 :D

【讨论】:

    【解决方案3】:

    您可以使用reduce

    第一个reduce是对数组进行汇总,并以年龄为key将其转换为对象。使用年龄作为键将更容易检查年龄是否已经存在。对象属性将有一个类似[2,true] 的数组值,其中第一个元素是年龄,第二个元素告诉年龄是否有重复项。使用Object.values 会将对象转换为数组。

    第二个reduce是形成想要的输出。

    let students = [{name: 'Abbey', age: 25 }, {name: 'Brian', age: 45 },{name: 'Colin', age: 25 }, {name: 'Dan', age: 78 }];
    
    let uniqueAges = Object.values(students.reduce((c, v) => {
      if (c[v.age] === undefined) c[v.age] = [v.age, true];
      else c[v.age][1] = false;
      return c;
    }, {})).reduce((c, v) => {
      if (v[1]) c.push(v[0]);
      return c;
    }, []);
    
    console.log(uniqueAges);

    【讨论】:

    • 感谢您的回答。然而,据我了解,reduce 以与循环相同的方式迭代元素,因此不会使函数的时间复杂度更快。我将编辑我的问题。
    • 好吧,如果您从 DB 中获取此信息,您可能需要查询唯一值。如果您使用的是 js,您可以通过迭代来实现您所需要的(嗯,据我所知。)
    • 请在您的回答中解释您的代码对 v 对象及其中的元组做了什么,为什么使用 Object.values,以及减少 c 数组的工作原理。你“可以使用reduce”是不相关的,同样的通用方法可以用于任何其他形式的循环。
    【解决方案4】:

    这是您可以做到的一种方法。我认为时间复杂度是O(n^2),其中n 是原始数组中的元素数量,m 是输出数组中唯一元素的数量。

    const students = [
      {name: 'Abbey', age: 25 }, 
      {name: 'Brian', age: 45 },
      {name: 'Colin', age: 25 }, 
      {name: 'Dan', age: 78 }
    ];
    
    const uniqueStudents = students.map(val => val.age)
      .sort()
      .reduce((current, next) => {
        return current.length === 0 ? [].concat(current, next)
          : current[current.length - 1] !== next ? [].concat(current, next)
            : current.slice(0, -1);
      }, []);
      
    console.log(uniqueStudents);

    【讨论】:

    • 据我所知,JavaScript 数组排序是 O(n log n)。
    • @גלעדברקן => 经过一些额外的阅读,你是正确的。在那种情况下,我真的不确定这实际上是什么复杂性。 :S
    • 我认为没有slice,主要的复杂性将是O(n log n)。但是对slice 的每次调用都会生成一个副本,因此具有O(n^2) 的潜力,因为每次调用slice 都会迭代当前。
    • @גלעדברקן => 好电话。到时候我会更新我的回复。
    【解决方案5】:

    ? 单次迭代最快的方式。

    const students = [
      {name: `Abbey`, age: 25}, 
      {name: `Brian`, age: 45},
      {name: `Colin`, age: 25}, 
      {name: `Dan`, age: 78},
      {name: `Dan`, age: 25}
    ]
    
    // no global variables
    function unique(key) {
      const occurrences = {}
      const list = {}
      return (result, next, index, {length}) => {
        const value = next[key]
        if (list[value]) {
          occurrences[value] = value
        }
        else {
          list[value] = value
          result.push(value)
        }
        return index === length - 1 ? result.filter(v => !occurrences[v]) : result
      }
    }
    
    const uniqueNames = students.reduce(unique(`name`), [])
    const uniqueAges = students.reduce(unique(`age`), [])
    
    console.log(uniqueAges)

    【讨论】:

    • 这实际上迭代了每个元素两次,对于 O(2n) 复杂性(与 O(n) 复杂性相同,但您的 确实 需要两次迭代 - 每个 reduce 回调一次,最后一次,当你filter)。这也无法区分强制转换为字符串时看起来相同的非字符串,例如54'54'
    猜你喜欢
    • 2014-03-06
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    • 2020-11-12
    • 2016-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多