【问题标题】:how to do a binary search on a vector to find element with certain id?如何对向量进行二进制搜索以查找具有特定 id 的元素?
【发布时间】:2017-07-20 16:06:08
【问题描述】:

我有一个已排序的向量,现在我想从该向量中找到具有特定 id 的元素。 std::binary_search 只是告诉我元素是否存在,所以我使用std::lower_bound

#include <vector>
#include <iostream>
#include <algorithm>

struct Foo {
    int id; 
    // ... more members ... //
    Foo(int id) : id(id) {} 
};

bool compareById(const Foo& a,const Foo& b) { return a.id < b.id; }

int main(){
    std::vector<Foo> vect;
    vect.push_back(10);
    vect.push_back(123);
    vect.push_back(0);
    std::sort(vect.begin(),vect.end(),compareById);
    int id_to_find = 1;
    std::vector<Foo>::iterator f = std::lower_bound(vect.begin(),vect.end(),Foo(id_to_find),compareById);
    if (f != vect.end() && f->id == id_to_find) { std::cout << "FOUND"; }
}

这有点工作,但我非常不喜欢我必须创建一个 Foo(id_to_find) 以将其传递给 std::lower_bound 然后我必须仔细检查我得到的元素是否真的是我正在寻找的元素。

我想我可以使用find_if 来避免创建那个多余的实例,但据我所知find_if 只是线性的并且不使用正在排序的向量。我有点惊讶我找不到适合该任务的算法,我已经在考虑编写自己的算法了。

这样做的惯用方式是什么?

【问题讨论】:

  • 你应该可以只传递 id_to_find1 而不是 Foo(id_to_find)
  • 你用对了。如果你想让它更容易写一个包装器。你总是需要检查返回值,除非你在元素不存在时抛出异常。
  • @0x499602D2 afaik 这不会有所作为,因为无论如何,即使我没有明确写出Foo,也会创建它。 (std::lower_bound 需要 const T&amp;
  • @NathanOliver 好吧,我只是不确定,我有点担心创建一个不必要的实例,因为在实际代码中 Foo 可能创建起来很昂贵(我可以只为此添加一个特殊的构造函数目的...很好地解决了问题:)

标签: c++ vector binary-search c++03


【解决方案1】:

您可以做几件事来让您的生活更轻松。首先,比较器的第一个参数和第二个参数不需要相同的参数。要求是第一个参数必须隐式转换为取消引用的迭代器,第二个参数需要隐式转换为传递给lower_bound的第三个参数的类型。

我们可以利用这一点,只需将int 作为第二个参数,这样您就不必构造Foo。这使我们可以创建 compareFooById 之类的:

bool compareFooById(const Foo& a,const int& b) { return a.id < b; }

然后我们现在可以像这样使用它:

std::vector<Foo>::iterator f = std::lower_bound(vect.begin(), vect.end(), id_to_find, compareFooById);

请注意,我确实在这里添加了一个新功能。您将compareById 用于std::sort,所以我不能只更改它,因为这会中断对sort 的调用。

现在,就必须检查您是否有一个有效的迭代器而言,您有几个选择。您可以编写一个包装函数,该函数采用对迭代器的引用来填充并在找到项目时返回。看起来像

template<typename It, typename Value, typename Comp
bool my_binary_search(It begin, It end, Value val, Comp c, It& ret)
{
    ret = std::lower_bound(begin, end, val, c);
    return ret != end;
}

然后你就这样称呼它

std::vector<Foo>::iterator f;
if(my_binary_search(vect.begin(), vect.end(), id_to_find, compareFooById, f))
    //do something with f.

另一种选择是在未找到该项目时抛出异常。在我看来,这不是你想做的事,除非找不到该项目是一个真正的例外情况。

【讨论】:

  • compareFooById 正是我想要的。当我第一次阅读您的答案时,我期望lower_bound 会进行隐式转换(这是我想要避免的),但that seems not to be the case。现在我想知道为什么要求“Type2 类型必须使得 T 类型的对象可以隐式转换为 Type2。”存在,因为我认为不需要它,而且显然没有发生隐式转换,但是将是一个不同的问题
  • @tobi303 比较器可能类似于bool compareFooById(const Foo&amp; a,const long long&amp; b) 并且由于Tlong long 不一样,它会失败。因为隐式转换是允许的,虽然没关系。
  • 是的,我只是不明白为什么它必须是隐式可转换的,因为据我了解,隐式转换没有执行,但 id_to_find 直接传递给 compareFooById 而不创建中间 @ 987654341@ 实例,不过我可能错了
  • @tobi303 但如果它们的类型不匹配,则必须进行隐式转换。如果我尝试将int 传递给需要const long long&amp; 的函数,那么编译器会从int 中创建一个临时long long 并将其提供给参考。这就是为什么他们说type2 必须是隐式可转换的。
  • 看着this output好像没有创建临时的Foo
猜你喜欢
  • 2022-01-22
  • 1970-01-01
  • 2010-09-27
  • 2017-04-09
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2020-04-16
  • 2015-05-10
相关资源
最近更新 更多