现在我有一个包含4列和无限行的网格。每个单元格可能都被一个Square占据,并且Square储存在ArrayList<Square> squares
中。
我希望能够找到(通过边线/角)连接到选定正方形的所有正方形,例如:
我正在使用一个递归函数,该函数检查选定正方形周围的正方形,然后对那些正方形进行相同的处理,但这导致某些正方形被检查两次,并且效率低下。
现在我正在使用类而不是函数来进行操作,并跟踪到目前为止在Set中已经检查过的对象,但是为了简化起见,希望将其保留在函数中。
要实施一种有效的算法,我将采取哪些步骤?
更新:正方形存储在ArrayList中,而不是2D数据结构中,因为我需要它们可以在程序中的其他位置轻松访问。当我需要找到相邻的正方形时,可以测试正方形之间的碰撞。
答案 0 :(得分:3)
简短版本
我认为深度优先搜索算法可能会对您有所帮助。
在您的情况下,每个图块都可以视为图形的一个节点,并且如果两个节点共享边或角,则两个节点之间存在一条边。
这里有一段很好的视频,介绍了我发现的算法的工作原理:Depth First Search on youtube
DFS算法可能与您尝试递归方法的方法非常相似,但是主要区别在于,随着算法的发展,您可以为访问的节点/切片“着色”。我建议您不要将探索的节点保留在单独的数据结构中,而应使其成为每个磁贴的属性。
然后,如果您绊倒了已经访问过的图块,就不会对其进行探索。如果已经浏览了当前节点的所有邻居,则返回到之前正在探索的节点,然后(递归)浏览其邻居,直到从开始算法的位置回溯到该节点。
与您的特定问题有关的更多详细信息
检测邻居
您提到过,正方形存储在ArrayList中。这可以。但是,如果没有正方形或包含对位于该位置的正方形实例的引用,则不会阻止您构建其单元为 null 的正方形二维数组。以我的拙见,这比寻找每对正方形之间的碰撞要容易得多(寻找我现在正在做的事情)。
您无需在程序中的其他任何地方使用这样的2D数组。我非常有信心,这样可以加快大量正方形的运算速度。
当然,还有其他数据结构可以使查找图节点之间的交点变得容易。例如,您可以一次构建一个adjacency matrix并将其用于任何后续计算,但是您不必这样做。
使用示例运行DFS
我将使用堆栈来跟踪我在图块探索中的位置。我将通过它们的坐标来指代轮胎。我们从中启动算法的单元格在您的图中以红色显示,并具有坐标(1,2)。
算法如下:
while (!stack.isEmpty()) {
currentTyle = stack.top();
boolean currentHasNeighborsToExplore = false;
for (n in neighbors of currentTyle) {
if (n is not explored) {
n is explored;
stack.add(n);
currentHasNeighborsToExplore = true;
break;
}
}
if (!currentHasNeighborsToExplore) {
stack.pop();
}
}
我们从您的初始轮胎(1,2)开始算法。
STEP 1
堆栈:[(1,2)
堆栈顶部是(1,2)
(1,2)的邻居n:(2,2)未开发
现在探究(2,2),我们将其添加到堆栈中并进行下一步
第2步
堆栈:[(1,2)(2,2)
堆栈顶部是(2,2)
(2,2)具有被探索的邻居n:(1,2)
(2,2)的邻居n:(3,1)未开发
(3,1)现在已被探索,我们将其添加到堆栈中并进行下一步
第3步
堆栈:[(1,2)(2,2)(3,1)
堆栈顶部是(3,1)
(3,1)具有被探索的邻居n:(2,2)
(3,1)的邻居n:(4,2)未开发
(4,2)现在已被研究,我们将其添加到堆栈中并进行下一步
第4步
堆栈:[(1,2,(2,2)(3,1)(4,2)
堆栈顶部是(4,2)
(4,2)的邻居n:(4,3)未开发
(4,3)现在已被探索,我们将其添加到堆栈中并进行下一步
第5步
堆栈:[(1,2)(2,2)(3,1)(4,2)(4,3)
堆栈顶部是(4,3)
(4,3)具有被探索的邻居n:(4,2)
(4,3)没有未开发的邻居,我们将其从堆栈中弹出并进行下一步
STEP 6
堆栈:[(1,2,(2,2)(3,1)(4,2)
堆栈顶部是(2,2)
(4,2)具有被探索的邻居n:(4,3)
(4,2)的邻居n:(5,1)未开发
现在探究(5,1),我们将其添加到堆栈中,然后进行下一步
下一步
在下一步中,(5,1)没有未开发的邻居,因为没有剩余的未开发邻居,它将像随后的每个样式一样从堆栈中弹出。
答案 1 :(得分:1)
我同意您在问题中应该明确的意见。但是,由于您询问“对某些正方形进行了两次检查”,因此我可以回答这一部分。您可以维护矩阵以跟踪已访问的单元格。最初,所有单元格都可以设置为0。一旦处理了给定的单元格,就可以将其设置为1。每次处理任何单元格时,只需使用访问矩阵检查它是否已被访问即可。
int visited[][] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
// you logic to process cell
if (visited[x][y] ==0) // check is not visited
visited[x][y] = 1; // mark cell as visited
else
//skip
答案 2 :(得分:0)
基本上,我没有理由实施任何复杂的算法,因为网格中的邻居非常容易计算,就像
+-------+-------+-------+
|x-1,y+1| x,y+1|x+1,y+1|
+-------+-------+-------+
|x-1, y| x, y|x+1, y|
+-------+-------+-------+
|x-1,y-1| x,y-1|x+1,y-1|
+-------+-------+-------+
您可以将squares
放在类似List<List<Square>>
的位置,并按索引访问它们
但是,如果要将它们保留在简单的List<>
中,您仍然可以通过将n
元素的n-th
索引计算为
+----->
| [(0,0) - 0 ] [(1,0) - 1st] [(2,0) - 2nd] [(3,0) - 3th]
| [(0,1) - 4th] [(1,1) - 5th] [(2,1) - 6th] [(3,1) - 7th]
v [(0,2) - 8th] [(1,2) - 9th] [(2,2) -10th] [(3,2) -11th]
// index for (2,1)
index_of_2_1 = (y * rows_count) + x = (1*4) + 2 = 6