我需要从大容器中找到许多对象。
我能想到的唯一方法就是在循环中一次只搜索一个项目的容器,然而,即使是一个有效的搜索,平均情况说“log n”(其中n是容器的大小),这给了我整个操作的“m log n”(其中m是我正在寻找的项目数)。
这对我来说似乎非常不理想,而且我可能需要在频繁的基础上做些事情,如果可能的话,我肯定希望改进。
这两个部分尚未实现,所以我愿意接受有关主容器格式,我正在寻找的项目的“列表”等建议,以及实际的搜索算法。
这些项目是复杂的对象,但搜索键只是一个简单的整数。
答案 0 :(得分:5)
哈希表基本上有O(1)查找。这为你提供了O(m)来查找m个项目;很明显,你不能比O(m)更快地查找m项,因为你需要得到结果。
答案 1 :(得分:4)
如果您纯粹在查找(您不需要有序元素)并且可以放弃一些内存,请尝试unordered_map(它是TR1,也在Boost中实现),它具有恒定时间摊销查找
在游戏引擎中,我们测试了std::map
和unordered_map
,而map
插入速度更快(如果我还记得),unordered_map
将其从水中吹走用于检索。我们在地图中有超过1000个元素用于缩放,与您可能正在执行的其他任务相比,这个元素相当低。
如果您需要订购元素,您的下一个赌注是std::map
,它具有您发布的查找时间,并保持订购的元素。通常,它使用的内存少于unordered_map
。
答案 2 :(得分:3)
如果您的容器是向量并且元素已排序,则可以使用std::lower_bound在O(log n)时间内进行搜索。如果您的搜索项目也已排序,您可以通过始终使用最后找到的迭代器作为搜索下一个迭代器的开始来进行小的优化,例如
vector<stuff> container;
vector<stuff>::iterator it = container.begin();
for (int i = 0; i < search_items.size() && it != container.end(); ++i)
{
it = std::lower_bound(it, container.end(), search_items[i]);
// make sure the found item is a match
if (it != container.end() && search_items[i] < *it)
it = container.end(); // break out early
}
if (it != container.end()) // found it!
答案 3 :(得分:0)
boost / tr1 unordered_map和unordered_set是由哈希表支持的容器,它允许您在分摊的持续时间内搜索[O(1)]
答案 4 :(得分:0)
我想如果你有一个已排序的容器和一个统一的项目分布,那么最有效的方法类型就是一个递归的二分法搜索,其执行路径有点像树 - 每当被搜索的所有对象都调用时,它自己调用两次在两半的两半中。
但是,如果你选择一个基于哈希表的容器(我认为提升无序集合),或类似的东西,那么查找可以是O(1),所以在循环中搜索确实无关紧要。
编辑: 请注意,std :: map和std :: set通常(总是?)使用rb-trees实现,因此只有log(n)用于查找。
答案 5 :(得分:0)
你确定 m log 2 ( n )实际上会出现问题吗?如果你使用的std::map
甚至比较大,那么实际比较的数量仍然很小 - 如果你在1,000,000的地图中查找10,000个元素,那么比较的数量应该是大约200,000或大约20每个目标元素的比较。如果你的密钥只是一个简单的整数,这真的不错。
如果你正在编写一些没有好键的东西,那么我会说使用boost :: unordered_map 。我首先使用std::map
实现它,对其进行概要分析,然后决定是否要进行下一次跳转到Boost。
答案 6 :(得分:0)
如果您经常对集合执行相同的投影,例如使用“42”键提取元素,则可以考虑将这些子集保留在存储桶中。您将在内部维护从键到具有该键的元素向量的散列映射,并将元素添加到相应的存储桶以及表示“所有内容”的主集合。提取元素子组是恒定时间(因为已经构建了相应的集合),维护存储桶的内存开销主要与数据集中唯一键的数量相关。
如果您拥有大量独特的键值,并且插入和删除更加昂贵,这种技术效果显然不大,但它对某些情况有利 - 我认为这至少值得一提。