四叉树最近邻算法

Posted

技术标签:

【中文标题】四叉树最近邻算法【英文标题】:Quadtree Nearest Neighbour Algorithm 【发布时间】:2014-01-17 05:16:51 【问题描述】:

我已经为 n 个点实现了一个四叉树结构,以及一个返回给定矩形内的点数组的方法。我似乎无法找到一种算法来有效地找到最接近另一个给定点的点。我错过了一些明显的东西吗?我认为递归解决方案是正确的方法?

我在 Objective C 中工作,但伪代码会很好。此外,我实际上存储的是经纬度数据,并且点之间的距离沿着一个大圆圈。

编辑: 这是我的树插入和细分代码

- (BOOL)insert:(id<PASQuadTreeDataPoint>)dataPoint 

    BOOL pointAdded = false;

    // If the point lies within the region
    if(CGRectContainsPoint(self.region, dataPoint.point)) 

        // If there are less than 4 points then add this point
        if(self.dataPoints.count < kMaxPointsPerNode) 
            [self.dataPoints addObject:dataPoint];
            pointAdded = true;
        
        else 

            // Subdivide into 4 quadrants if not already subdivided
            if(northEast == nil) [self subdivide];

            // Attempt to add the point to one of the 4 subdivided quadrants
            if([northEast insert:dataPoint]) return true;
            if([southEast insert:dataPoint]) return true;
            if([southWest insert:dataPoint]) return true;
            if([northWest insert:dataPoint]) return true;
        
    

    return pointAdded;


- (void)subdivide 

    // Compute the half width and the origin
    CGFloat width = self.region.size.width * 0.5f;
    CGFloat height = self.region.size.height * 0.5f;
    CGFloat x = self.region.origin.x;
    CGFloat y = self.region.origin.y;

    // Create a new child quadtree with the region divided into quarters
    self.northEast = [PASQuadTree quadTreeWithRegion:CGRectMake(x + width, y, width, height)];
    self.southEast = [PASQuadTree quadTreeWithRegion:CGRectMake(x + width, y + height, width, height)];
    self.southWest = [PASQuadTree quadTreeWithRegion:CGRectMake(x, y + height, width, height)];
    self.northWest = [PASQuadTree quadTreeWithRegion:CGRectMake(x, y, width, height)];

编辑: 已编写此代码来查找包含该点的最小节点(叶):

-(PASQuadTree *)nodeThatWouldContainPoint:(CGPoint)point 

    PASQuadTree *node = nil;

    // If the point is within the region
    if (CGRectContainsPoint(self.region, point)) 

        // Set the node to this node
        node = self;

        // If the node has children
        if (self.northEast != nil) 

            // Recursively check each child to see if it would contain the point
            PASQuadTree *childNode = [self.northEast nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.southEast nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.southWest nodeThatWouldContainPoint:point];
            if (!childNode) childNode = [self.northWest nodeThatWouldContainPoint:point];
            if (childNode) node = childNode;
        
    

    return node;

靠近但没有雪茄!

【问题讨论】:

【参考方案1】:

找到以您的搜索点为中心且恰好位于该矩形内的另一个点的最小正方形(您需要进行 logn 次搜索)。

设 x 为到另一点的距离。

然后找到一个正方形内的所有点,其边为 2x 并以您的第一个点为中心。对于这个正方形内的每个点,计算到搜索点的距离并找到最近的。

更新:如何找到一个以搜索点为中心且恰好包含另一个点的正方形?

找一个随机点。设到该随机点的距离为 x。找到以搜索点为中心的大小为 x 的正方形内的所有点。如果该正方形内的点数不为零,则随机选择一个点并重复。如果没有点,则将搜索方块的大小增加到 (2-0.5)*x (在下一步中为 (2-0.25)*x 等等。

【讨论】:

感谢 ElKamina,将试一试并在问题中发布我的代码。我不确定的部分是每个节点将有四个点,并且只有在节点容量已满时才会被分割成更小的矩形。其中一些点是否也是最近的点? 好的,有代码可以找到包含该点的最小区域(参见原始问题)。努力以优雅的方式找到周围的矩形,另外需要考虑每个节点中包含的 4 个点。任何指针都会有所帮助。干杯戴夫。 @MagicBulletDave 我只是在使用四叉树的功能,它有效地“返回给定矩形内的点数组”。我没有使用任何其他属性。我已经用更多解释更新了我的答案。 谢谢 ElKamina,有点厚:o) 每个孩子都需要有一个指向其父母的指针。通过这个简单的更改,您基本上可以向上遍历。当你找到最小的矩形时,如果没有足够的点,你可以向上检查你的搜索条件。另一种解决方案是叶节点与指向其兄弟节点的指针连接,从而构建一个双链表。你基本上预处理四叉树,在搜索过程中,你找到四叉树中最小的网格,然后你在 LinkedList 中左右移动,以找到符合搜索条件的正确网格。

以上是关于四叉树最近邻算法的主要内容,如果未能解决你的问题,请参考以下文章

在Unity中使用四叉树算法绘制地形

[地图开发][算法及数据结构]四叉树原理

Apache Spark - 实现分布式四叉树

[译]2D空间中使用四叉树Quadtree进行碰撞检测优化

2D空间中使用Quadtree四叉树进行碰撞检测优化

Leetcode 558.四叉树交集