【问题标题】:c++ std::vector search for valuec++ std::vector 搜索值
【发布时间】:2013-01-02 15:15:56
【问题描述】:

我正在尝试优化 std::vector“搜索” - 基于索引的迭代向量并返回与“搜索”条件匹配的元素

struct myObj {
   int id;
   char* value;
};

std::vector<myObj> myObjList;

创建几千个具有唯一 id 和值的条目,并将它们推送到向量 myObjList

检索与id 匹配的myObj 的最有效方法是什么。 目前我的索引迭代如下:

for(int i = 0; i < myObjList.size(); i++){
   if(myObjList.at(i).id == searchCriteria){
    return myObjList.at(i);
   }
}

注意:searchCriteria = int。所有元素都有独特的id。 以上方法可以完成这项工作,但可能不是最有效的方法。

【问题讨论】:

  • 你的元素是否以任何方式排序?
  • 列表是在读取数据时创建的,不会以任何方式排序。
  • 在尝试真正优化涉及std::vector 的任何代码时要考虑的第一点是使用operator[] 而不是at,这每次都会进行完全不必要的范围检查。
  • 感谢 Christian - 我将在今天晚些时候测试“operator[]”,以获取最低的挂果 :)

标签: c++ search vector iterator


【解决方案1】:

C++ 标准库有一些抽象算法,它们赋予 C++ 一种功能性,正如我所说,它让您可以更专注于搜索标准,而不是如何实现搜索自己。这适用于许多其他算法。

您正在寻找的算法是std::find_if,这是一个通过迭代器范围的简单线性搜索。

在 C++11 中,您可以使用 lambda 来表达您的条件:

std::find_if(myObjList.begin(), myObjList.end(), [&](const myObj & o) {
    return o.id == searchCriteria;
});

当没有可用的 C++11 时,您必须提供一个谓词(函数对象 (=functor) 或函数指针),如果提供的实例是您要查找的实例,则该谓词返回 true。仿函数的优点是它们可以被参数化,在你的情况下,你想用你正在寻找的 ID 参数化仿函数。

template<class TargetClass>
class HasId {
    int _id;
public:
    HasId(int id) : _id(id) {}
    bool operator()(const TargetClass & o) const {
        return o.id == _id;
    }
}

std::find_if(myObjList.begin(), myObjList.end(), HasId<myObj>(searchCriteria));

此方法返回一个迭代器,该迭代器指向找到的第一个符合条件的元素。如果没有这样的元素,则返回结束迭代器(它指向向量的末尾,而不是最后一个元素)。所以你的函数可能看起来像这样:

vector<myObj>::iterator it = std::find_if(...);

if(it == myObjList.end())
    // handle error in any way
else
    return *it;

【讨论】:

  • OP 可能还没有 lambda。
  • 谢谢两位,添加了非 C++11 的解决方案。
  • 注意:lambda示例中的return语句缺失
【解决方案2】:

使用std::find_if

引用页面上有一个示例。

这是一个更适合您的问题的工作示例:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct myObj
{
   int id;
   char* value;

   myObj(int id_) : id(id_), value(0) {}
};

struct obj_finder
{
    obj_finder(int key) : key_(key)
    {}

    bool operator()(const myObj& o) const
    {
        return key_ == o.id;
    }

    const int key_;
};

int main () {
  vector<myObj> myvector;
  vector<myObj>::iterator it;

  myvector.push_back(myObj(30));
  myvector.push_back(myObj(50));
  myvector.push_back(myObj(100));
  myvector.push_back(myObj(32));

  it = find_if (myvector.begin(), myvector.end(), obj_finder(100));
  cout << "I found " << it->id << endl;

  return 0;
}

而且,如果你有 C++11 可用,你可以使用 lambda 使其更加简洁:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

struct myObj
{
   int id;
   char* value;

   myObj(int id_) : id(id_), value(0) {}
};

int main ()
{
  vector<myObj> myvector;
  vector<myObj>::iterator it;

  myvector.push_back(myObj(30));
  myvector.push_back(myObj(50));
  myvector.push_back(myObj(100));
  myvector.push_back(myObj(32));

  int key = 100;

  it = find_if (myvector.begin(), myvector.end(), [key] (const myObj& o) -> bool {return o.id == key;});
  cout << "I found " << it->id << endl;

  return 0;
}

【讨论】:

  • 为了清楚起见,您可能希望在示例中使用 OP 的定义。但没关系,很久以前就得到了我的支持。也许int 可以通过值来捕获,但是很好。
【解决方案3】:

这并不是您问题的真正答案。其他回答的人都给出了很好的答案,所以我没有什么要补充的。

我想说的是,尽管您的代码不是非常惯用的 C++。当然,真正惯用的 C++ 会使用::std::find_if。但即使您没有::std::find_if,您的代码仍然不是惯用的。我将提供两次重写。一个是 C++11 重写,第二个是 C++03 重写。

首先,C++11:

for (auto &i: myObjList){
   if(i.id == searchCriteria){
      return i;
   }
}

二、C++03:

for (::std::vector<myObj>::iterator i = myObjList.begin(); i != myObjList.end(); ++i){
   if(i->id == searchCriteria){
      return *i;
   }
}

遍历任何类型的 C++ 容器的标准方法是使用迭代器。向量可以按整数索引,这很好。但是,如果您不必要地依赖这种行为,如果您以后应该更改数据结构,那么您自己就会变得更加困难。

【讨论】:

  • 传入的数据结构被一些 ISO 标准刻在石头上——所以现在可能可以依靠它保持不变。感谢您的所有意见!
  • @narkis:这也比你做的要快,而且习惯性意味着其他了解 C++ 的人会更快地掌握循环。 :-)
  • @Omnifarious:您已经编写了基于范围的 for 循环,就好像 i 是一个迭代器,但它不是。我想你的意思是for (auto i : myObjList) { if(i.id == searchCriteria) return i; }for (auto &amp;i : myObjList) { same thing; }
【解决方案4】:

如果对 id 进行了排序,您可以执行二进制搜索(在 stl 中还有一个函数 binary_search)。如果它们不是什么都不会表现得更好,但您仍然可以使用 stl 以更短的方式编写代码(使用 find_if)。

【讨论】:

  • 谢谢 - 我会看看当向量最初用“myObj”填充时是否有合理的方法来按 id 排序 - 毫无疑问,这也会产生开销。感谢您的建议!
猜你喜欢
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 2021-08-07
  • 2011-01-01
  • 2016-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多