【问题标题】:binary search in array of object javascript [closed]javascript对象数组中的二进制搜索[关闭]
【发布时间】:2021-11-22 09:26:00
【问题描述】:

我有一个对象数组:

array = [
  {
    name:'alex',   
    number:36783712773
  },
  {
    name:'gabe',   
    number:99938873,
  },
and so on like this
]

我知道二分搜索的基本概念及其工作原理,但互联网上的大多数示例都处理数字数组,因此我无法弄清楚在对象数组中按姓名和电话进行搜索。

我可以从后端进行搜索,但我不想对可以在 javascript 中轻松处理的事情进行不必要的 HTTP 调用。

提前致谢。

【问题讨论】:

  • 您必须选择namenumber 对特定数组进行二分搜索,然后您要搜索的数组必须 首先按该值排序。如果你不能满足这个要求(如果你想按 2 个不同的字段搜索,你需要维护 2 个单独的排序数组),那么你可能是barking up the wrong tree
  • 好吧,假设我只想按名称搜索,如果我理解正确,我必须先按名称排序,然后像在简单的二分搜索函数中那样进行比较
  • 好吧,谢谢大家,我想我得到了需要的东西,再次感谢您的时间和技巧。
  • 请注意@trincot 的分析不幸被测试用例的错误实现所破坏。他的二分搜索比.find更快,即使是1000个条目,一旦修复。
  • 投票重新打开,虽然这要求提供教程参考,但它也要求直接帮助。来自 trincot 的答案显示了一种有用的方法。虽然问题应该显示了 OP 的尝试,但如果没有,这仍然是一个合理的问题。

标签: javascript arrays algorithm object binary-search


【解决方案1】:

下面是二分查找的实现。

在此答案的先前版本中,生成测试用例时存在巨大错误。与线性搜索相比,更新后的版本在使用小型数组时已经显示出二分搜索的优势。

这是一个脚本,用于计算对象数组上的线性搜索和二分搜索的执行时间。首先是 10 个对象的数组,然后是 100、1000,最后是 10 000:

function binarySearch(array, target) {
    let lo = 0, hi = array.length;
    while (lo < hi) {
        let mi = (lo + hi) >> 1;
        let diff = target - array[mi].number;
        if (diff === 0) return array[mi];
        else if (diff < 0) hi = mi;
        else lo = mi + 1;
    }
}

function linearSearch(array, target) {
    return array.find(o => o.number === target);
}

function test(searchFunc, array, searchValues, times) {
    let accTime = 0;
    for (let time = 0; time < times; time++) { // Time the execution several times
        let now = performance.now();
        for (let number of searchValues) {
            let match = searchFunc(array, number);
            if (!match) throw "err";
        }
        accTime += performance.now() - now;
    }
    return accTime / times; // ... and take average
}

for (let length = 10; length < 1e5; length *= 10)
{
    let array = Array.from({length}, (_, number) =>
        ({ name: "alex", ismember: true, number: number * 100 + 13 })
    );
    let searchValues = Array.from({length: 10000}, () =>
        Math.floor(length * Math.random()) * 100 + 13
    );
    // First a dry run
    let linearSearchTime = test(linearSearch, array, searchValues, 2);
    let binarySearchTime = test(binarySearch, array, searchValues, 2);
    // The real run
    linearSearchTime = test(linearSearch, array, searchValues, 5);
    binarySearchTime = test(binarySearch, array, searchValues, 5);
    console.log({length, binarySearchTime, linearSearchTime});
}

不同的可搜索属性

如果您想有时按一个属性,有时按另一个属性使用二进制搜索,那么您需要创建单独的“索引”来引用您的数据,每个索引都按相关属性排序。

例如:

let data = [
    { name: "alex", number: 13 },
    { name: "helen", number: 10 },
    { name: "zoe", number: 11 },
    { name: "willy", number: 12 },
];

let index = Object.fromEntries(["name", "number"].map(prop => [
    prop,
    data.map(o => [o[prop], o])
        .sort(([a], [b]) => (a > b) - (a < b))
]));

function binarySearch(array, target) {
    let lo = 0, hi = array.length;
    while (lo < hi) {
        let mi = (lo + hi) >> 1;
        let key = array[mi][0]; 
        if (key === target) return array[mi][1]; // the actual data
        else if (key > target) hi = mi;
        else lo = mi + 1;
    }
}

// demo
console.log(binarySearch(index.name, "willy"));
console.log(binarySearch(index.name, "zoe"));
console.log(binarySearch(index.name, "alex"));
console.log(binarySearch(index.name, "helen"));
console.log(binarySearch(index.number, 13));
console.log(binarySearch(index.number, 12));
console.log(binarySearch(index.number, 11));
console.log(binarySearch(index.number, 10));

【讨论】:

  • 嗯,这很有趣,直到现在我才知道线性搜索的威力,这让我撤消了我刚刚编写的所有代码 :( 好吧,猜猜必须这样做。谢谢 Trincot 提供的代码示例并让我意识到我将要创建和部署的错误。
  • ...一旦修复,数字就会非常非常不同。即使在 1000 个元素时,二分搜索也会获胜。
  • 哎呀,这真是一个糟糕的错误。
  • 没关系@trincot 它发生在我们所有人身上。我只需按我最喜欢的按钮 [ctrl+z]。
  • @trincot Hail Github
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多