我正在尝试构建KD Tree(静态情况)。我们假设点在x和y坐标上排序。
对于偶数递归深度,该集合被分成两个子集,垂直线穿过中间x坐标。
对于奇数递归深度,该集合被分成两个子集,水平线穿过中间y坐标。
可以根据x / y坐标从排序集确定中值。这一步我在每次拆分之前做的。我认为这会导致树的构造缓慢。
非常感谢你的帮助和耐心......
请参阅示例代码:
class KDNode
{
private:
Point2D *data;
KDNode *left;
KDNode *right;
....
};
void KDTree::createKDTree(Points2DList *pl)
{
//Create list
KDList kd_list;
//Create KD list (all input points)
for (unsigned int i = 0; i < pl->size(); i++)
{
kd_list.push_back((*pl)[i]);
}
//Sort points by x
std::sort(kd_list.begin(), kd_list.end(), sortPoints2DByY());
//Build KD Tree
root = buildKDTree(&kd_list, 1);
}
KDNode * KDTree::buildKDTree(KDList *kd_list, const unsigned int depth)
{
//Build KD tree
const unsigned int n = kd_list->size();
//No leaf will be built
if (n == 0)
{
return NULL;
}
//Only one point: create leaf of KD Tree
else if (n == 1)
{
//Create one leaft
return new KDNode(new Point2D ((*kd_list)[0]));
}
//At least 2 points: create one leaf, split tree into left and right subtree
else
{
//New KD node
KDNode *node = NULL;
//Get median index
const unsigned int median_index = n/2;
//Create new KD Lists
KDList kd_list1, kd_list2;
//The depth is even, process by x coordinate
if (depth%2 == 0)
{
//Create new median node
node = new KDNode(new Point2D( (*kd_list)[median_index]));
//Split list
for (unsigned int i = 0; i < n; i++)
{
//Geta actual point
Point2D *p = &(*kd_list)[i];
//Add point to the first list: x < median.x
if (p->getX() < (*kd_list)[median_index].getX())
{
kd_list1.push_back(*p);
}
//Add point to the second list: x > median.x
else if (p->getX() > (*kd_list)[median_index].getX())
{
kd_list2.push_back(*p);
}
}
//Sort points by y for the next recursion step: slow construction of the tree???
std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByY());
std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByY());
}
//The depth is odd, process by y coordinates
else
{
//Create new median node
node = new KDNode(new Point2D((*kd_list)[median_index]));
//Split list
for (unsigned int i = 0; i < n; i++)
{
//Geta actual point
Point2D *p = &(*kd_list)[i];
//Add point to the first list: y < median.y
if (p->getY() < (*kd_list)[median_index].getY())
{
kd_list1.push_back(*p);
}
//Add point to the second list: y < median.y
else if (p->getY() >(*kd_list)[median_index].getY())
{
kd_list2.push_back(*p);
}
}
//Sort points by x for the next recursion step: slow construction of the tree???
std::sort(kd_list1.begin(), kd_list1.end(), sortPoints2DByX());
std::sort(kd_list2.begin(), kd_list2.end(), sortPoints2DByX());
}
//Build left subtree
node->setLeft( buildKDTree(&kd_list1, depth +1 ) );
//Build right subtree
node->setRight( buildKDTree(&kd_list2, depth + 1 ) );
//Return new node
return node;
}
}
答案 0 :(得分:5)
找到中位数的排序可能是这里最糟糕的罪魁祸首,因为那是O(nlogn),而问题在O(n)时间内是可解的。您应该使用nth_element:http://www.cplusplus.com/reference/algorithm/nth_element/。这将平均找到线性时间的中位数,之后您可以在线性时间内分割矢量。
向量中的内存管理也需要花费很多时间,特别是对于大向量,因为每次向量的大小加倍时,所有元素都必须移动。您可以使用向量的保留方法为新创建的节点中的向量保留足够的空间,因此在使用push_back添加新内容时,它们无需动态增加。
如果您绝对需要最佳性能,则应使用较低级别的代码,取消向量并保留普通数组。第N个元素或“选择”算法随时可用,并且不难写自己:http://en.wikipedia.org/wiki/Selection_algorithm
答案 1 :(得分:3)
不是您的问题的答案,但我强烈推荐论坛 http://ompf.org/forum/
他们在那里进行了一些很好的讨论,以便在各种情况下快速进行kd-tree构造。也许你会在那里找到一些灵感。
修改强>
OMPF论坛已经失败,尽管目前可以在http://ompf2.com/
答案 2 :(得分:2)
优化kd-tree的一些提示:
答案 3 :(得分:1)
你的第一个罪魁祸首是排序以找到中位数。这几乎总是K-d树构建的瓶颈,在这里使用更高效的算法真的会有所回报。
但是,每次分割和传递元素时,你也构建了一对可变大小的矢量。
在这里,我推荐好的单链表。链表的优点在于,您只需将next
指针更改为指向子指针的根指针而不是父指针,即可将元素从父元素传输到子元素。
这意味着在构造期间没有任何堆开销将元素从父节点传输到子节点,只是聚合要插入到根节点的元素的初始列表。这也应该有奇迹,但如果你想要更快,你可以使用固定分配器有效地为链表(以及树)分配节点,并具有更好的连续性/缓存命中。
最后但同样重要的是,如果您参与了需要K-d树的密集型计算任务,那么您需要一个分析器。测量你的代码,你会看到罪魁祸首,确切的时间分布。