我试图确定哪种结构更适合进行多个点的半径搜索,kd-tree还是octree?这已经在这个问题中提到过,但没有答案。在我的看法中,由于octree对于叶子节点有固定的大小,因此可以计算出需要访问的分支,而对于kd-tree,则需要迭代地访问分支,直到覆盖半径。
我试图确定哪种结构更适合进行多个点的半径搜索,kd-tree还是octree?这已经在这个问题中提到过,但没有答案。在我的看法中,由于octree对于叶子节点有固定的大小,因此可以计算出需要访问的分支,而对于kd-tree,则需要迭代地访问分支,直到覆盖半径。
我个人实现了这两种数据结构,但是为了这个目的,我会选择八叉树。我发现使用八叉树可以更容易地获得更高效的结果。我之所以这么说是因为我认为在这些微妙的区别中,实现者比数据结构更重要。但是我认为对于大多数人来说,优化八叉树会更容易。
其中一个原因是K-D树本质上更深,因为它们是二叉树,每次只在一个维度上进行分割。如果你正在寻找叶子节点上的精确匹配元素,例如用于射线/三角形相交的单一、明确的路径,那么这种更深的性质可能会有所帮助。当深度合理的树与搜索质量的概念相匹配时,它非常有用。
如果你正在寻找最大半径内的最近点,而你花费大部分时间只是在树中上下移动,从叶子节点到父节点再到兄弟节点、祖父节点、父节点的兄弟节点等等,那么拥有一个深度合理的树就不太有用了。如果你可以以缓存友好的方式访问所有内容,并且你可以轻松地使八叉树缓存友好,例如将所有8个子节点连续存储,那么你只需要这样做:
struct OctreeNode
{
// Index of first child node. To get to the 4th node,
// we just access nodes[first_child+3], e.g.
int first_child;
...
};
总之,如果这是两个选择,我会投票给八叉树。对于这种类型的接近搜索,你不一定希望八叉树太深。即使我们必须查看比最优解更多的点,使用较浅的树可能比频繁上下遍历树更好。如果在叶子节点中存储的点是连续的,那么可以通过后处理来实现。
请注意,在两种解决方案中,您都必须查看兄弟节点。最接近点并不一定是位于同一叶节点中的点。还有一些情况,仅使用三维网格实际上可以非常有效,具体取决于数据的性质,因为使用三维网格时,您甚至不必从子节点到父节点再到兄弟节点。如果将网格单元的内存开销减少到仅为32位索引,则三维网格似乎会占用大量内存,但实际上不必如此。在这种情况下,100x100x100网格所需的内存不到4兆字节。
在我的项目中,我使用八叉树进行范围搜索,它运行效率高且易于实现。虽然我从未将其与KD-Tree进行比较。 据我所知,对于三维数据,在kd树中进行此操作的最坏时间复杂度为O(n^(2/3)),而Octree只能保证O(n)。因此,如果您关心最坏时间复杂度,请选择KD Tree。(我不关心最坏时间复杂度,如果我知道在我的数据集中这永远不会发生。)
Operation K-D Tree Octree
Build Tree 7 ms 12 ms
K-NN Search (k=1) 124 ms 390 ms
K-NN Search (k=10) 124 ms 395 ms
K-NN Search (k=100) 134 ms 573 ms
Radius Search (r=0.01 m) 1789 ms 126 ms
Radius Search (r=0.1 m) 1799 ms 129 ms
Radius Search (r=1 m) 2011 ms 415 ms