【问题标题】:Kd tree: data stored only in leaves vs stored in leaves and nodesKd树:仅存储在叶子中的数据与存储在叶子和节点中的数据
【发布时间】:2012-12-26 21:16:31
【问题描述】:

我正在尝试实现一个 Kd 树以在 C++ 中执行最近邻和近似最近邻搜索。到目前为止,我遇到了最基本的 Kd 树的 2 个版本。

  1. 一种,数据存储在节点和叶子中,例如here
  2. 一种,数据仅存储在叶子中,例如here

它们似乎基本相同,具有相同的渐近特性。

我的问题是:有什么理由选择一个而不是另一个?

到目前为止,我想出了两个原因:

  1. 在节点中存储数据的树也浅了 1 级。
  2. 只在叶子中存储数据的树更容易 实现delete data函数

在决定制作哪一个之前,我还应该考虑其他一些原因吗?

【问题讨论】:

  • @Boris Strandjev,谢谢!
  • 为什么是第二个原因?我想即使使用第二种方法,您也可以在中间节点中存储一些距离数据?
  • @BorisStrandjev 在1.st的方法中,如果删除了一个节点,就需要找一个替换节点。这可以通过搜索以该节点为根的子树来实现。在 2. nd 方法中,您可以删除叶子
  • 但是中间节点的数据还需要更新吗?
  • 是的,这是更好、更勤奋的方法。但是第二棵树让您可以选择使用草率的方法,而不修改中间节点。

标签: c++ computer-science kdtree


【解决方案1】:

您可以将节点标记为已删除,并将任何结构更改推迟到下一次树重建。 k-d-trees 会随着时间的推移而退化,因此您需要频繁地重建树。 k-d-trees 非常适用于不改变的低维数据集,或者您可以轻松负担重建(近似)最优树的地方。

至于实现树,我建议使用简约的结构。我通常不使用节点。我使用一组数据对象引用。轴由当前搜索深度定义,无需将其存储在任何地方。左右邻居由数组的二叉搜索树给出。 (否则,只需添加一个 byte 数组,它是数据集大小的一半,用于存储您使用的轴)。加载树由专门的 QuickSort 完成。理论上它是O(n^2) 最坏的情况,但如果采用很好的启发式算法(例如 median-of-5),您可以非常可靠地获得O(n log n),并且具有最小的恒定开销。

虽然它对于 C/C++ 没有那么多,但在许多其他语言中,您会为管理大量对象付出相当大的代价。 type*[] 是您能找到的最便宜的数据结构,特别是它不需要大量的管理工作。要将元素标记为已删除,您可以null 它,并在遇到null 时搜索两边。对于插入,我首先将它们收集在缓冲区中。当修改计数器达到阈值时,重建。

这就是它的全部意义:如果你的树重建起来真的很便宜(就像使用几乎预先排序的数组一样便宜!)那么频繁地重建树并没有什么坏处。 对简短的“插入列表”进行线性扫描对 CPU 缓存非常友好。跳过nulls 也很便宜。

如果您想要更动态的结构,我建议您查看 R*-trees。它们实际上旨在平衡插入和删除,并将数据组织在面向磁盘的块结构中。但即使对于 R-tree,也有报道称保留插入缓冲区等以推迟结构更改可以提高性能。在许多情况下批量加载也有很大帮助!

【讨论】:

  • 非常感谢您的详细解释。但是有几点我不清楚。 1.你是什么意思你不使用节点? 2. 关于我的问题,你能更具体一点吗?两棵树的比较
  • 您实际上可以在没有node 数据类型的情况下实现kd-tree。总内存成本:n 指针。你的代码越简单,通常越快。我还想传达的是:您可以实现好或慢。没有一个更好的规则,但这取决于您根据您的特定需求实施它们的程度。
猜你喜欢
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 2020-06-10
  • 2013-02-16
  • 1970-01-01
  • 2016-04-01
相关资源
最近更新 更多