我的算法中的缺陷在哪里找到0和1的数组中1s的最大连通区域的大小?

时间:2016-08-22 04:00:21

标签: c# algorithm

例如,如果数组类似于

1 1 0 0
0 1 1 0
0 0 1 0
1 0 0 0

然后答案是5

我有辅助功能

// Returns the size of the region of 1s containing the point (x0, y0). 
// For example, if mat = 0 0 1
//                       1 0 0
//                       1 1 1
// then max_connected_region(0,0,mat) = 0, 
//      max_connected_region(2,0,mat) = 1,
//  and max_connected_region(0,1,mat) = 4
static int max_connected_region(int x0, int y0, int[,] mat)
{  
    if(mat[x0,y0] == 0)
        return 0;
    var surroundings = (new int[][] {
        new int[] { x0 - 1, y0 }, new int[] {x0 + 1, y0 }, 
        new int[] { x0 - 1, y0 + 1}, new int[] { x0, y0 + 1 }, new int[] {x0 + 1, y0 + 1},
        new int[] { x0 - 1, y0 - 1}, new int[] { x0, y0 - 1 }, new int[] {x0 + 1, y0 - 1} }
     ).Where(pair => pair[0] >= 0 && pair[0] < mat.GetLength(0) && pair[1] >= 0 && pair[1] < mat.GetLength(1));
    int count = 1;
    foreach(var pair in surroundings)
        count += max_connected_region(pair[0], pair[1], mat);
    mat[x0,y0] = 0;
    return count;
}

以及我如何找到n x m数组(n行,m列)中的最大连接数正在使用它

   int max_connections = 0;
   for(int j = 0; j < n; ++j)
   {
       for(int i = 0; i < m; ++i)  
       {
           if(matrix[i,j] == 0)
               continue;
           int connections = max_connected_region(i,j,matrix);
           if(connections > max_connections)
               max_connections = connections;
       }
   }

这个程序让我在测试用例中超时或超出范围,我无法弄清楚原因。

1 个答案:

答案 0 :(得分:2)

作为noted in the comments,您的算法正在重新审视已经检查过的数组元素,将其置于无限循环中。

你实际上有一个程序语句似乎试图避免这种情况,但你在递归调用后执行。所以它没有任何有用的效果。如果您只是在执行递归调用的循环之前移动它,您的算法将起作用:

static int max_connected_region(int x0, int y0, int[,] mat)
{
    if (mat[x0, y0] == 0)
        return 0;
    var surroundings = (new int[][] {
        new int[] { x0 - 1, y0 }, new int[] {x0 + 1, y0 }, 
        new int[] { x0 - 1, y0 + 1}, new int[] { x0, y0 + 1 }, new int[] {x0 + 1, y0 + 1},
        new int[] { x0 - 1, y0 - 1}, new int[] { x0, y0 - 1 }, new int[] {x0 + 1, y0 - 1} }
     ).Where(pair => pair[0] >= 0 && pair[0] < mat.GetLength(0) && pair[1] >= 0 && pair[1] < mat.GetLength(1));
    int count = 1;
    mat[x0, y0] = 0;
    foreach (var pair in surroundings)
        count += max_connected_region(pair[0], pair[1], mat);
    return count;
}

我注意到你的算法具有破坏性。也就是说,它修改了传递给它的数组。对于您的场景,这可能是可以接受的 - 在最坏的情况下,这意味着调用者需要确保它传递其数据的副本。但如果这是某种库方法,您可以考虑自己制作副本,或者使用适当大小的bool[,]来跟踪算法已经访问过的位置。

我也觉得在方法的每次迭代中为surroundings分配一个全新的数组可能不是最好的方法。如果您打算在更大的数据集上运行此算法,那么拥有一个包含有效偏移量的静态数组可能更有意义,然后只需要一个显式的for循环遍历该数组以进行递归调用。这将在您访问每个数组元素时最小化额外的内存分配和垃圾收集开销。

进行这些更改,方法及其支持类成员看起来更像是这样:

static int max_connected_region2(int x0, int y0, int[,] mat)
{
    return max_connected_region2_impl(x0, y0, (int[,])mat.Clone());
}

static int max_connected_region2_impl(int x0, int y0, int[,] mat)
{
    if (mat[x0, y0] == 0)
        return 0;

    int count = 1;

    mat[x0, y0] = 0;
    for (int i = 0; i < adjacentCells.Length; i++)
    {
        int[] pair = adjacentCells[i];
        int x1 = pair[0] + x0, y1 = pair[1] + y0;

        if (x1 >= 0 && x1 < mat.GetLength(0) && y1 >= 0 && y1 < mat.GetLength(1))
        {
            count += max_connected_region2_impl(x1, y1, mat);
        }
    }
    return count;
}

private static readonly int[][] adjacentCells =
{
    new [] { -1, 0 }, new [] { 1, 0 }, new [] { -1, 1 }, new [] {0, 1 },
    new [] { 1, 1 }, new [] { -1, -1}, new [] { 0, -1 }, new [] { 1, -1 }
};