我现在有一个工作实现,它映射范围的键,如下所示:
class Range {
public:
Range(int from, int to = -1) : _from(from), _to( to >= 0 ? to : from) {}
bool operator < (const Range& item) {
return _to < item._from;
}
bool operator == (const Range& item) {
return item._from >= _from && item._to <= _to;
}
private:
int _from, _to;
};
typedef std::map<Range, MappedType> my_map_type;
这件事很酷我可以这样做:
my_map_type m;
m[Range(0, 20)] = Item1;
m[Range(30,40)] = Item2;
my_map_type::iterator it = m.find(15);
assert(it->second == Item1);
it = m.find(40);
assert(it->second == Item2);
it = m_find(25);
assert(it == m.end());
但我需要比 std :: map 更快的地图实现。插入可以很慢,但发现必须非常快。试过 boost :: unordered_map ,但我无法使用 Range 类(虽然我已经为Range对象实现了 boost :: hash_value ) 。 查找不返回任何内容(查找时,运算符== 甚至不会被调用,我觉得很奇怪)
想法?
答案 0 :(得分:5)
您无法使用哈希表执行此操作,operator==
的定义无法与哈希函数兼容:在代码Range(10, 20) == Range(15, -1)
中,哈希函数无法返回相同的哈希。
通常,哈希和相等必须兼容:x == y
必须暗示hash(x) == hash(y)
。当然反过来也不正确。
所以你需要一些基于比较的结构,比如基于树的map
。您可以定义一个正确的相等比较器,而不是使用可能会给您带来问题的损坏的operator==
,并使用map::lower_bound
来完成您正在尝试的操作。
如果它太慢,您可以使用已排序的向量并使用std::lower_bound
。搜索时间是O(log n),它与std::map
渐近相同,但实际上更快(没有指针追逐,更好的位置)。但是它具有线性更新(插入/删除)时间。
否则你可能想看一下专门的结构,比如interval trees,但它们没有在STL中实现(可能是Boost?)。
无论如何,隐式Range(int)
构造函数具有误导性并且可能有害。您应该将其声明为explicit
并使用find(Range(40))
代替find(40)
。
答案 1 :(得分:1)
无论是使用std::map
还是使用任何其他容器,您尝试执行的操作都无效。您的operator <
和operator ==
与这些运营商的传统要求不符:
a == b
和a == c
,那么b == c
:在您的情况下,[1,2] == [0,100]
和[98,99] == [0,100]
,但显然是[1,2] != [98,99]
。a < b
为真,则b < a
为假:在您的情况下,[2,4] < [1,3]
为真,但[1,3] < [2,4]
也为真。因此,在某些情况下,您的std::map
实施也会失败。
不仅如此,std::map
只返回一个范围,而可以预期给定的项目可能在地图的多个范围内。
如果您可以安全地假设没有任何范围重叠,那么仅根据from
值对范围进行排序,使用upper_bound
提取范围小于from
的范围您要查找的值,并与该范围的to
进行比较,以确定它是否是实际匹配。
答案 2 :(得分:0)
您正在使用std :: map&lt;&gt;通常实现为红黑树。 boost :: multi_index容器还提供基于红黑树的容器,但是带有压缩节点(小于指针的大小),所以它会比std :: map&lt;&gt;更快。由于工作量较小。
另一个选择是使用哈希,这样当你没有哈希冲突时,有些人会查找O(1)。