如何跟踪C ++中的访问点

时间:2018-03-08 17:34:16

标签: c++ algorithm

我在c ++中遇到问题,必须跟踪遍历中访问的点。重点是,

struct Point {
  int x;
  int y;
};

我首先想到的解决方法是使用类似

的东西
std::set<Point> visited_points;

或者

std::map<Point, bool> visited_points;

但是,我是c ++的初学者,我意识到你必须实现一个比较,我不知道该怎么做。当我问起时,有人告诉我说在这样的问题中使用地图是“过度杀伤”。他说更好的解决方案是做一些像

这样的事情
std::vector<std::vector<bool>> visited_points;

他说{​​{1}}不是最好的解决方案,因为使用矢量更快。

我想知道为什么在样式和性能方面使用双向量更好。是因为实施比较对于点来说很难吗?双向量对我来说很难看,而且我认为它看起来比使用集合或地图更丑陋。这真的是解决这个问题的最好方法吗,还是有一个我不了解的更好的解决方案?

1 个答案:

答案 0 :(得分:3)

如果有人以抽象的方式问你,&#34;跟踪我访问过的物品的最佳方式是什么?&#34;那么你会被原谅回复&#34;使用std::unordered_set<Object>&#34; (通常称为C ++以外语言的哈希表)。这是一个很简单的答案,如果你对这些对象一无所知,这通常是正确的。毕竟,哈希查找是(预期的)O(1),实际上通常非常快。

有一些注意事项,最重要的一点是您需要能够为每个对象计算哈希值。 C ++标准库(尚未)带有用于计算任意对象哈希的框架,甚至不包括POD,并且将对象呈现为字符串以便能够利用std::hash<std::basic_string>通常太过分了工作(当然,除非对象已经是一个字符串)。

如果你无法弄清楚如何为你的对象编写一个哈希函数,你可能会考虑使用一个有序的关联容器(也就是一个平衡的BST)。但是,这不是一个好主意。不是因为编写比较函数很困难。编写比较函数通常是微不足道的,特别是对于POD;你可以利用std::tuple为元素类型都具有可比性的每个元组实现比较函数这一事实。

有序关联容器的真正问题在于它们的开销很大。元素访问速度很慢:O(log n),而不是O(1),常量也不小。并且维护平衡树所需的簿记数据比双指针哈希表节点大得多(甚至对于小对象来说也是如此)。所以有序的关联容器真的只有在你需要能够按顺序遍历它们时才有意义。一般来说,&#34;访问&#34;地图根本不需要遍历 - 它们仅用于查找。

有序和无序容器都有另一个问题:容器中的对象是单独的动态内存分配(API要求对容器中对象的引用必须是稳定的),因此随着时间的推移,各个对象最终会分散动态内存,导致大量缓存未命中。

但是,实际上,甚至在您开始考虑散列对象以便将它们保存在哈希集中是多么容易(或困难)之前,您应该考虑要跟踪的对象的性质。特别是,它们可以很容易地用小( - )整数索引吗?如果是这样,你可以使用一个位向量,每个可能的对象一位。这对于访问速度(绝对是O(1))和空间来说都是一种有效的表示,并且它对于内存缓存来说是最佳的。

如果您的对象很容易编号,那么位向量将是一个很有吸引力的选择。每个对象一个比特(字面上)比哈希图少两个数量级的空间,所以除非你期望你的访问地图非常稀疏(在需要访问地图的算法中很少这种情况),它会去成为一个大胜利。

如果您遇到问题,我收集的问题与跟踪矩形阵列(如游戏板或图像)中访问的点有关,很明显,位向量方法可以很好地解决。确实,您需要两个级别的索引(除非您将两个索引缩减为一个整数,如果您知道维度,这很容易),但这不会增加很多开销。

虽然对于它的想法有多么怀疑,但C ++标准库特殊情况std::vector<bool>确实有点向量。这使得无法创建指向向量的单个元素的本机指针(这就是为什么许多人认为std::vector<bool>是一个hack),并且当您尝试将其用作向量时会产生一些其他奇怪的问题。但如果您想要的只是一个位掩码 - 就像访问过的地图一样 - 那么这是一个非常好的解决方案。

C ++还提供了真正的位向量 - std :: bitset - 但不幸的是,它们需要在编译时知道它们的大小。 Boost提供了dynamic_bitset,这是一种事后写的std::vector<bool>,所以它也值得一看。