std :: map <tuple <int,int> .lower_bound / upper_bound前缀搜索,但没有找到后缀的最小/最大元素</tuple <int,int>

时间:2012-11-15 16:12:42

标签: c++ stl c++11

我正在构建一个使用std::map<tuple<...>>作为查找数据结构的类。我希望能够在地图上进行前缀搜索,以查找在元组内共享特定前缀的所有元素。例如:

using namespace std;
map<tuple<int,int,int,string>,Value> index;
index.insert(make_tuple(1,1,1,"a"),Value());
index.insert(make_tuple(1,1,2,"b"),Value());
index.lower_bound(make_tuple(1,1,std::numeric_limits<int>::min(),""));

这正是我需要的我必须手动(对于非POD类型)找出最小值。更糟糕的是,当我想找到具有特定前缀的最高元素时(我这样做),我必须找到某个类型的最大元素值,在string的情况下,这是相当有问题的。

理想情况下,我可以提供map.find/lower_bound/upper_bound(不是std :: find,这是地图上的线性复杂性),并且单独比较不考虑后缀,但不幸的是,这是不可能的,很可能因为前缀搜索或多或少是唯一明智的应用程序。

我认为选项会修改map在运行时使用的比较(我认为风格非常糟糕)或者为元组内部的所有类型实现numeric_limits等价物。

在地图/集上进行前缀搜索是否有更好的选择?

2 个答案:

答案 0 :(得分:2)

在定义时,您可以将“静态”比较仿函数传递给maptuple<...>上的默认排序是类型的词典排序。

将密钥类型扩展为允许将字段标记为maxmin值的密钥类型,并提供接受它们的排序算法。

即,像这样:

template<typename T>
struct InfiniteExtensionOf
{
  enum ExtendedOrder {NegInfinity=-1, Normal=0, PosInfinity=1};
  ExtendedOrder order;
  T value;
  bool operator<(InfiniteExtensionOf const& other) const
  {
    if (order != other.order)
      return order<other.order;
    return value < other.value;
  }
  template<typename U>
  InfiniteExtensionOf( U&& u ):order(Normal),value(std::forward(u)) {}
  InfiniteExtensionOf( InfiniteExtensionOf&& other ):order(other.order),value(std::move(other.value)) {}
  InfiniteExtensionOf( InfiniteExtensionOf& other ):order(other.order),value(other.value) {}
  InfiniteExtensionOf( InfiniteExtensionOf const& other ):order(other.order),value(other.value) {}
  InfiniteExtensionOf( ExtendedOrder& eo ):order(eo), value() {}
  InfiniteExtensionOf( ExtendedOrder&& eo ):order(eo), value() {}
  InfiniteExtensionOf( ExtendedOrder const& eo ):order(eo), value() {}
};

然后像这样关键:

map<tuple<InfiniteExtensionOf<int>, InfiniteExtensionOf<int>, InfiniteExtensionOf<int>, InfiniteExtensionOf<string>>, Value> myMap;

哪个接受tuple s而没有InfiniteExtensionOf参数(因为这样的元组隐式转换,我希望),你可以在调用时轻松指定+ inf或-inf作为特定字段的值lower_boundupper_bound

...

请注意,如果lower_bound采用了模板参数,并且只要求它以与现有顺序一致的方式与地图中的元素兼容,那么这将不那么麻烦。 :)但遗憾的是,事实并非如此。

答案 1 :(得分:1)

你可以做的是不直接使用密钥类型,而是选择类型的三态版本:

  1. 如果设置了该值,则比较仅使用该值。
  2. 如果设置了small标志,则认为该值小于所有其他值。
  3. 如果设置了large标志,则认为该值大于任何其他值。
  4. 要查找下限,您可以搜索,例如使用small值:

    map.lower_bound(srd::make_tuple(
        tristate<int>(17),
        tristate<std::string>(tristate_small)));
    

    这是针对具有由intstd::string组成的密钥的地图,每个密钥可能被小值或大值替换。