【问题标题】:How to check if object with equal value is in a container in logarithmic time如何在对数时间内检查具有相等值的对象是否在容器中
【发布时间】:2018-02-02 19:40:49
【问题描述】:

我想以对数时间查看容器中是否有相等值的对象。

我想拥有以下功能:

const a = [];

const el1 = {name: 'name1', directive: 'directive1'};
const el2 = {name: 'name2', directive: 'directive2'};
const el3 = {name: 'name3', directive: 'directive3'};
const b = {name: 'name1', directive: 'directive1'};

a.push(el1);
a.push(el2);
a.push(el3);


if(a.some(el => (el.name === b.name && el.directive === b.directive ))) {
  console.log("YES!");
} else {
  console.log("NO!");
}

这给了我想要的结果。但是,这是 O(N) 时间。

const s = new Set();

const el1 = {name: 'name1', directive: 'directive1'};
const el2 = {name: 'name2', directive: 'directive2'};
const el3 = {name: 'name3', directive: 'directive3'};
const b = {name: 'name1', directive: 'directive1'};

s.add(el1);
s.add(el2);
s.add(el3);

if(s.has(b)) {
  console.log("YES!");
} else {
  console.log("NO!");
}

这是 O(logN),但结果不是我想要的。

那么我可以使用哪种 Javascript 数据结构来在上面的代码中打印 YES 并且复杂度为 O(logN)? (我不想自己实现数据结构)

【问题讨论】:

  • 是什么让你认为有一个?
  • 我问有没有。我会假设有,因为这似乎是一个非常通用的用例。
  • 如果你的数据已经排序并且你进行了二分搜索呢?
  • 您的要求是可能的,但提供最佳方法需要有关您允许的对象类型的更多信息。它们总是只是包含带有值字符串的普通对象的浅数组吗?
  • 您是要解决这个特定的[{name, directive}] 案例还是更一般的案例?

标签: javascript algorithm time-complexity


【解决方案1】:

您可以使用以名称为键的 Map,其中对应的值是一组指令:

const elements = [
    {name: 'name1', directive: 'directive1'},
    {name: 'name2', directive: 'directive2'},
    {name: 'name3', directive: 'directive3'}
];

const map = new Map(elements.map(({name}) => [name, new Set]));
elements.forEach(({name, directive}) => map.get(name).add(directive));

const b = {name: 'name1', directive: 'directive1'};

if (map.has(b.name) && map.get(b.name).has(b.directive)) {
    console.log("YES!");
} else {
    console.log("NO!");
}

【讨论】:

    【解决方案2】:

    假设:数组a 按字典顺序排序,即a[i]<a[j] 无论何时a[i].name<a[j].name || (a[i].name==a[j].name && a[i].description<a[j].description))

    这是一个具有对数复杂度的二分搜索:

    const a = [];
    for (i = 0; i < 100000; i++) {
      si = String(i);
      el = {
        name: 'name' + si,
        directive: 'directive' + si
      };
      a.push(el);
    }
    
    const b = {
      name: 'name70000',
      directive: 'directive70000'
    };
    
    
    var c = 0; // counter 
    
    // binary search
    function binary_search(a, b) {
      c++;
      var l = 0;
      var r = a.length - 1;
      if (l > r) {
        return false;
      }
      m = Math.floor((l + r) / 2);
      if ((a[m].name < b.name) || (a[m].name == b.name && a[m].directive < b.directive)) {
        return binary_search(a.slice(m + 1), b);
      } else if ((a[m].name > b.name) || (a[m].name == b.name && a[m].directive > b.directive)) {
        return binary_search(a.slice(0, m), b);
      } else {
        return true;
      }
    }
    
    
    
    found = binary_search(a, b);
    console.log(found, "in " + String(c) + " steps");

    【讨论】:

    • 最佳性能不一定是对数时间,对吧?
    • @JasonB 一般来说,没有。如果数组是排序的,你可以进行二分搜索以获得对数复杂度
    【解决方案3】:

    我们可以使用Array 作为数据结构。考虑到我们可以使用递归对数时间函数,通过确定父字段部分的端点并递归来沿多个维度进行排序(或查找插入/删除的位置)。

    假设我们的维度是脚手架:name -&gt; directive -&gt; third_property

    首先按照name对数组进行排序。现在找到log n 时间中的每个name 部分,并根据directive 仅对该部分进行快速排序,third_property 类似。然后找到一个元素,使用类似的二分搜索,首先找到name属性的范围,然后在directive的范围内使用二分搜索,依此类推。

    或者通过为每个可以排序的元素创建一个键并进行二进制搜索来绕过所有这些:element.key = element.name + element.directive :)

    【讨论】:

      【解决方案4】:

      不要看这个答案的负分。使用它并享受它。 O(logN).?


      它返回 false,因为它们(el1b)是不同的对象引用。您可以通过el1 填写b,但如果您想要一种简单的方法来使用O(logN) 进行搜索,您可以使用我的以下代码:

      var orderObj = function(unordered){
         const ordered = {};
         Object.keys(unordered).sort().forEach(function(key) {
             ordered[key] = unordered[key];
          });
          return ordered;
      }
      Set.prototype.addIt = function(item){s.add(JSON.stringify(orderObj(item)));}
      Set.prototype.hasIt = function(item){return s.has(JSON.stringify(orderObj(item)));}
          
      //-------------------------------------
      
      const s = new Set();
      
      const el1 = {name: 'name1', directive: 'directive1'};
      const el2 = {name: 'name2', directive: 'directive2'};
      const el3 = {name: 'name3', directive: 'directive3'};
      
      const b = {name: 'name1', directive: 'directive1'};
      const c = {directive: 'directive3', name: 'name3'};
      
      s.addIt(el1);
      s.addIt(el2);
      s.addIt(el3);
      
      s.hasIt(b)
       ?console.log("YES!")
       :console.log("NO!");
      
      s.hasIt(c)
       ?console.log("YES!")
       :console.log("NO!");

      更新:

      我更新了我的答案,现在它支持不同顺序的对象。正如您在上面的示例中看到的那样,cel3 的顺序不同,但脚本返回 Yes 结果;)

      【讨论】:

      • 我认为 OP 知道这一点,但是当 b 是具有相同属性值的不同对象时,它需要它工作。
      • 关于您基于 JSON 的更新解决方案:请注意,对象属性可能以不同的字符串化顺序出现,但语义上的属性顺序与 JavaScript 对象无关,因此即使如此,它们也应该被视为作为匹配。
      • @trincot 我认为这通常不是一个好的解决方案,但我们不能在插入和查询之前对 JSON 字符串进行排序吗?
      猜你喜欢
      • 2012-05-11
      • 2016-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 2023-03-07
      • 1970-01-01
      • 2012-06-10
      相关资源
      最近更新 更多