高效的算法来查找连接的组件

时间:2019-01-21 07:56:02

标签: java algorithm graph-algorithm

现在我有一个包含4列和无限行的网格。每个单元格可能都被一个Square占据,并且Square储存在ArrayList<Square> squares中。

我希望能够找到(通过边线/角)连接到选定正方形的所有正方形,例如:

Example

我正在使用一个递归函数,该函数检查选定正方形周围的正方形,然后对那些正方形进行相同的处理,但这导致某些正方形被检查两次,并且效率低下。

现在我正在使用类而不是函数来进行操作,并跟踪到目前为止在Set中已经检查过的对象,但是为了简化起见,希望将其保留在函数中。

要实施一种有效的算法,我将采取哪些步骤?

更新:正方形存储在ArrayList中,而不是2D数据结构中,因为我需要它们可以在程序中的其他位置轻松访问。当我需要找到相邻的正方形时,可以测试正方形之间的碰撞。

3 个答案:

答案 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