查找矩阵中连通集总数的算法

时间:2012-06-28 21:16:02

标签: algorithm graph graph-theory

我想知道我应该在这里应用哪种算法。 DFS会这么做吗?

给出2-d矩阵。找到该矩阵中连接集的总数。

连接集可以被定义为其中提到1并且在该集合中具有至少一个其他小区的小区组,它们与它们共享邻居关系。其中包含1并且没有周围邻居的小区可以被认为是其中包含一个小区的集合。可以将邻居定义为在8个可能方向(即,N,W,E,S,NE,NW,SE,SW方向)上与给定小区相邻的所有小区。一个小区不是它自己的邻居。

例如:

1 0 0 1

0 0 1 0

0 0 1 0

1 0 0 1

连接数量为3

0 0 1 0 0 1 0 0

1 0 0 0 0 0 0 1

0 0 1 0 0 1 0 1

0 1 0 0 0 1 0 0

1 0 0 0 0 0 0 0

0 0 1 1 0 1 1 0

1 0 1 1 0 1 1 0

0 0 0 0 0 0 0 0

连接数量为9。

11 个答案:

答案 0 :(得分:7)

我认为您不需要将其视为一般图形问题并应用任何算法,例如BFSDFS

您需要对矩阵进行三次扫描。

扫描1:

从顶部开始

  1. 给每个数字1加1..n,在你的例子中,第一行将在该步骤之后
      

    1 0 0 2

  2. 转到下一行,行中的每1行检查左边的邻居是否为非0
    • 如果非0采用左侧的值
    • 如果0检查前一行中的非0邻居并采用最左侧的值
    • 如果所有这些都是0,那么只需将1添加到目前为止给出的最大数
  3. 重复2直到最后一行已处理
  4. ,您的示例应如下所示

    1 0 0 2
    0 0 2 0
    0 0 2 0
    3 0 0 2
    

    扫描2:

    从底部开始 检查每个邻居是否与最左边邻居的号码相同,以及与下面一行中邻居的号码相同

    基本上如果你有这样的矩阵

      

    1 0 2

         

    1 0 2

         

    0 1 0

    检查确保一组具有真正相同的数字

    扫描3:

    计算矩阵中唯一非0条目的数量

答案 1 :(得分:3)

Connected-component labeling算法旨在标记连接的元素组(包括4连接和8连接)

答案 2 :(得分:1)

您想使用disjoint set数据结构和算法。这将为每个连接的组件选择一个唯一的代表,您可以在最后计算。

为了有效地评估哪些元素是邻居,您可以逐行扫描矩阵,维护上一行(连续1)的段列表,同时确定哪些段当前行与它们相邻。

答案 3 :(得分:1)

有3组连接。彼此相邻的所有1个被认为是一个单独的集合。 a[1,4]a[2,3]a[3,3]a[4,4]的所有1个形成一个集合,一个a[1,1]形成一个集合,一个a[4,1]形成一个集合集。

答案 4 :(得分:1)

Pythonic实现,更易理解的代码:

# sea is 2 D array of 0 and 1s we have to find 1's group surrounded by 0's
def dfs(sea, i, j, b, h, visited):
    surround = ((-1, -1), (0, -1), (1, -1),
                (-1, 0), (1, 0),
                (-1, 1), (0, 1), (1, 1)
                )
    if can_visit(sea, i, j, b, h, visited):
        for s in surround:
            visited[(i, j)] = 1
            dfs(sea, i + s[0], j + s[1], b, h, visited)


def can_visit(sea, i, j, b, h, visited):
    if i >= 0 and j >= 0 and i < b and j < h:
        if (i, j) not in visited and sea[i][j] == 1:
            return True


def find_island(sea):
    visited = {}
    h = len(sea)
    count = 0
    for i, row in enumerate(sea):
        b = len(row)
        for j, item in enumerate(row):
            if can_visit(sea, i, j, b, h, visited):
                count += 1
                dfs(sea, i, j, b, h, visited)
    return count


sea = [[1, 1, 0, 0, 0],
       [0, 1, 0, 0, 1],
       [1, 0, 0, 1, 1],
       [0, 0, 0, 0, 0],
       [1, 0, 1, 0, 1]
       ]

print find_island(sea)

答案 5 :(得分:0)

扫描矩阵1秒。找到一个时,调用一个递归函数,如果它尚未被识别为一个,则标记其连接的组件。使用递归查找连接的组件。在某处快速查找,告诉您某个给定节点是否已被识别为连接组件,以避免识别连接组件2x,并避免在遍历连接组件时出现无限循环。

答案 6 :(得分:0)

如果你想通过你的矩阵(没有额外的记忆)来做,请按照以下步骤进行:

将扫描仪位置设置为[0,0]

  1. 将计数器设为零。
  2. 逐行扫描当前扫描仪位置的矩阵(并逐个单元格)并找到一个1并将扫描仪位置设置为1之后的下一个元素,如果没有{{1}转到第6步。
  3. 将相关的一个设置为1并递归查找其所有counter+2个邻居,并将其设置为1
  4. count + 2
  5. 转到第2步。
  6. 输出count = count + 1
  7. PS:很清楚扫描仪的位置是否大于算法结束的矩阵大小(我没有写这个以防止混淆)。

答案 7 :(得分:0)

这并不像它看起来那么难。事实上,这非常像教授在第一年的计算机科学专业分配的东西。所以,如果这是作业,你应该这样标记。

然而,解决方案相当容易。

for (int y = 0; y < arr.height(); y++)
{
   for (int x = 0; x < arr.width(); x++)
   {
      if (arr[x][y] == 1)
      {
         if (CheckIfConnected(x, y, arr))
         {
            connectedPositionsX.Add(x);
            connectedPositionsY.Add(y);
         }
      }
   }
}

其中connectedPositions是链接列表或您想要存储的任何设置。

arr是一个2D数组,包含您在上面指定的类型的矩阵。

CheckIfConnected也可以相当简单地实现。

bool CheckIfConnected(int x, int y, int[][]arr)
    {
       if (arr.width() >= 2) || (arr.height() >= 2)
       {
          if ((x < arr.width()) && (x >= 0) && (y < arr.height()) && (y >= 0))
          {
            if ((x-1) >= 0) //West
            {
                if (arr[x-1][y] == 1)
                {
                    adjCount[x-1][y] += 1;
                    return true;
                }
            }
            if (((x-1) >= 0) && ((y-1) >= 0)) //Northwest
            {
                if (arr[x-1][y-1] == 1)
                {
                    adjCount[x-1][y-1] += 1;
                    return true;
                }
            }
            if ((y-1) >= 0) //North
            {
                if (arr[x][y-1] == 1)
                {
                    adjCount[x][y-1] += 1;
                    return true;
                }
            }
            if (((x+1) < arr.width()) && ((y-1) >= 0)) //Northeast
            {
                if (arr[x+1][y-1] == 1)
                {
                    adjCount[x+1][y-1] += 1;
                    return true;
                }
            }
            if ((x+1) < arr.width()) //East
            {
                if (arr[x+1][y] == 1)
                {
                    adjCount[x+1][y] += 1;
                    return true;
                }
            }

            //I'll let you implement Southeast to Southwest on your own,
            //the pattern is clear now.
          }
       }
       return false;
    }

从那里,您知道在网格中每个位置找到配对的次数。这有助于您跟踪您的连接。

2D数组adjCount中的计数会为您追踪这一点。

你也可以通过修改Dijkstra的算法来递归地为你做。既然你提到了DFS(深度优先搜索),我假设你的教授或老师希望你这样解决它。

在那种情况下:

这是Dijkstra在伪代码中的算法: http://en.wikipedia.org/wiki/Dijkstra“s_algorithm

希望有所帮助!干杯!

答案 8 :(得分:0)

只需在值为1的每个节点上以递归方式继续搜索东,东南,南和西西方向。 如果对访问函数的调用是一个新的调用而不是来自递归,则增加连接的组件。

import java.util.Scanner;

public class Solution {

    public static void visit(int[][] ar, boolean[][] v,int i, int j){
        int size = ar.length;
        if(ar[i][j] == 1){
            v[i][j] = true;
            if(j>0 && i<size-1){
                visit(ar,v,i+1,j-1); // SouthWest
            }
            if(i<size-1){
                visit(ar,v,i+1,j); // South
                if(j < size-1)
                    visit(ar,v,i+1,j+1); // SouthEast
            }
            if(j<size-1)
                visit(ar,v,i,j+1); // East
        }
    }

    public static void main(String[] args) {
        int[][] ar;
        int count = 0;
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        ar = new int[n][n];
        boolean[][] v = new boolean[n][n];
        for(int i=0; i<n ; i++) {
            for(int j=0; j<n; j++){
                ar[i][j] = sc.nextInt();
                v[i][j] = false;
            }
        }

        for(int i=0; i<n ; i++) {
            for(int j=0; j<n; j++){                
                if(ar[i][j] == 1 && !v[i][j]){
                    count++;
                    visit(ar,v,i,j);
                }
            }
        }
        System.out.println(count);
    }
}

答案 9 :(得分:0)

我有一个类可以帮助您找到2D阵列中连接组件的总数。我的班级不仅可以为您提供总数,还可以为您提供聚类并为您提供可视化。您可以注释掉您不需要的部分。请在(java)中查看此课程:https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/ConnectedComponetns.java

答案 10 :(得分:0)

对于python尝试:

import numpy
from scipy import ndimage

data = [[0, 0, 1, 0, 0, 1, 0, 0],
        [1, 0, 0, 0, 0, 0, 0, 1],
        [0, 0, 1, 0, 0, 1, 0, 1],
        [0, 1, 0, 0, 0, 1, 0, 0],
        [1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 0, 1, 1, 0],
        [1, 0, 1, 1, 0, 1, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 0]]

label, index = ndimage.label(data, numpy.ones((3, 3)))

print(label, index)

[[0 0 1 0 0 2 0 0]
 [3 0 0 0 0 0 0 4]
 [0 0 5 0 0 6 0 4]
 [0 5 0 0 0 6 0 0]
 [5 0 0 0 0 0 0 0]
 [0 0 7 7 0 8 8 0]
 [9 0 7 7 0 8 8 0]
 [0 0 0 0 0 0 0 0]] 9