我正在制作一个程序,您可以点击地图查看其周围区域的“特写视图”,例如在Google地图上。
当用户点击地图时,它会获得他们点击的X和Y坐标。
让我们假设我有一系列布尔值这些特写视图图片的位置:
public static boolean[][] view_set=new boolean[Map.width][Map.height];
//The array of where pictures are. The map has a width of 3313, and a height of 3329.
程序搜索文件夹,其中图像被命名为在地图上拍摄地点的X和Y坐标。该文件夹包含以下图像(以及更多,但我只列出五个):
2377,1881.jpg, 2384,1980.jpg, 2389,1923.jpg, 2425,1860.jpg, 2475,1900.jpg
这意味着:
view_set[2377][1881]=true;
view_set[2384][1980]=true;
view_set[2389][1923]=true;
view_set[2425][1860]=true;
view_set[2475][1900]=true;
如果用户点击了X和Y,例如2377,1882,那么我需要程序来确定哪个图像最接近(在这种情况下答案是2377,1881)。
任何帮助将不胜感激, 感谢。
答案 0 :(得分:3)
你的boolean[][]
对于这个问题不是一个好的数据结构,至少如果它不是真的很密集(例如通常在周围的3×3或5×5平方中有特写视图的点可用)。
您需要具有最近邻搜索的二维地图。此目标的有用数据结构是 QuadTree 。这是一个4级树,用于表示空间数据。 (我在这里描述了“带点数据的区域QuadTree”。)
基本上,它将一个矩形分成四个大小相等的矩形,如果其中有多个点,则进一步细分每个矩形。
因此树中的节点就是其中之一:
(在实现中,我们可以用其父指针中的空指针替换空叶节点。)
要找到一个点(或“一个点将在其中的节点”),我们从根节点开始,查看我们的点是否是分界点的北/南/东/西,然后转到相应的子节点节点。我们继续这个,直到我们到达一些叶子节点。
为了添加一个新点,我们要么结束一个空节点 - 然后我们可以把新点放在这里。如果我们最终在一个已经有一个点的节点上,创建四个子节点(通过拆分矩形)并将两个点添加到适当的子节点。 (这可能是相同的,然后递归重复。)
对于最近邻搜索,我们将结束一个空节点 - 然后我们备份一个级别,并查看该父级的其他子节点(比较每个距离)。如果我们到达其中有一个点的子节点,我们会测量搜索点到此点的距离。如果它小于到边缘或节点的距离,我们就完成了。否则,我们也必须查看相邻节点中的点,并在此处比较结果,取最小值。 (我认为,我们最多只能看四点。)
要删除,找到一个点后,我们将其节点设为空。如果父节点现在只包含一个点,我们将其替换为单点叶节点。
搜索和添加/删除是O(深度)时间复杂度,其中最大深度受 log((地图长度+宽度)/结构中两点的最小距离)的限制 ,平均深度取决于点的分布(例如到下一个点的平均距离),或多或少。
所需空间取决于点的数量和树的平均深度。
此数据结构存在一些变体(例如,仅当节点中存在多个X点时才拆分节点,或者不一定在中间拆分),以优化空间使用并避免树的深度过大
答案 1 :(得分:2)
根据用户点击的位置,您可以使用Dijkstra搜索搜索最近的图像。 基本上,您开始在点击的图像位置周围搜索越来越大的矩形。当然,你只需搜索这些矩形的边界,因为你已经搜索了身体。一旦找到图像,该算法就应该停止。
伪代码:
int size = 0
Point result = default
while(result == default)
result = searchRectangleBoundary(size++, pointClicked)
function Point searchRectangleBoundary(int size, Point centre)
{
point p = {centre.X - size, centre.Y - size}
for i in 0 to and including size
{
if(view_set[p.X + i][p.Y]) return { p.X + i, p.Y}
if(view_set[p.X][p.Y + i]) return { p.X, p.Y + i}
if(view_set[p.X + i][p.Y + size]) return { p.X + i, p.Y + size}
if(view_set[p.X + size][p.Y + i]) return { p.X + size, p.Y + i}
}
return default
}
请注意我为了简洁而省略了范围检查。
有一个小问题,但根据应用程序,它可能不是问题。它不使用欧几里德距离,而是曼哈顿指标。所以它不一定能找到最接近的图像,而是最多可以找到2倍平方根的图像。
答案 2 :(得分:1)
基于
......你正在以错误的方式解决这个问题。 @ JBSnorro的回答是一个非常优雅的方法,可以找到大海捞针(350点)(1100万点),但实际上,为什么要创造干草堆呢?
根据我对你的问题的评论,为什么不使用Pair<Integer,Integer>
类来表示坐标,将它们存储在一个集合中,然后扫描它们?它更简单,更快速,更少内存消耗,并且方式对于更大的地图更具可扩展性(假设感兴趣的点是稀疏的...这似乎是一个明智的假设,因为它们是感兴趣的点)。
..相信我,计算欧几里德距离〜425次绕着一个1100万的值boolean[][]
徘徊,寻找感兴趣的25,950中的1值(特别是在最坏的情况下分析)。
如果你真的不为每次扫描〜425个值而感到兴奋,那么(i)你的OCD比我更强(:P
); (ii)您应该查看nearest neighbour search算法。
答案 3 :(得分:-1)
我不知道你是否要求这个。如果用户点为P1 {x1,y1},并且您想要计算其到P2 {x2,y2}的距离,则使用毕达哥拉斯定理计算距离
distance^2 = (x2-x1)^2 + (y2-y1)^2
如果你只想知道最近的,你可以避免计算平方根(距离越小,方形也越小,所以它也会为你提供相同的效果)。