【问题标题】:can counting contiguous regions in a bitmap be improved over O(r * c)?在 O(r * c) 上计算位图中的连续区域可以改进吗?
【发布时间】:2012-08-13 14:11:40
【问题描述】:

给您一张由卫星拍摄的表面图像。该图像是一个位图,其中水标有“。”土地标有“*”。相邻的'*'组形成一个岛。 (如果两个 '*' 是水平、垂直或对角相邻的,则它们是相邻的)。你的任务是在位图中打印岛屿的数量。

示例输入:-

.........**
**......***
...........
...*.......
*........*.
*.........*

输出:- 5

这是我的实现,它占用O(r * c) 空间和O(r * c) 空间,其中 r 是总数。行数,c 是列数。

#include <stdio.h>
#define COLS 12

void markVisted(char map[][COLS], int visited[][COLS], int row, int col, int rowCount)
{
    if((row < 0) || (row >= rowCount) || (col < 0) || (col >= COLS) || (map[row][col] != '*') || (visited[row][col] == 1)) return;

    visited[row][col] = 1;

    //calling neighbours
    markVisted(map, visited, row+1, col, rowCount);
    markVisted(map, visited, row, col+1, rowCount);
    markVisted(map, visited, row-1, col, rowCount);
    markVisted(map, visited, row, col-1, rowCount);
    markVisted(map, visited, row+1, col+1, rowCount);
    markVisted(map, visited, row-1, col-1, rowCount);
    markVisted(map, visited, row-1, col+1, rowCount);
    markVisted(map, visited, row+1, col-1, rowCount);
}
int countIslands(char map[][COLS], int visited[][COLS], int rowCount)
{
    int i, j, count = 0;
    for(i=0; i<rowCount; ++i){
        for(j=0; j<COLS; ++j){

            if((map[i][j] == '*') && (visited[i][j] == 0)){
                ++count;
                markVisted(map, visited, i, j, rowCount);
            }
        }
    }
    return count;
}

int main()
{
    char map[][COLS] = {
                    "*..........",
                    "**........*",
                    "...........",
                    "...*.......",
                    "*........*.",
                    "..........*"               
                    };
    int rows = sizeof(map)/sizeof(map[0]);
    int visited[rows][COLS], i, j;  

    for(i=0; i<rows; ++i){
        for(j=0; j<COLS; ++j) visited[i][j] = 0;
    }

    printf("No. of islands = %d\n", countIslands(map, visited, rows));


    return 0;
}

请为这个问题提出一些更好的逻辑
此外,欢迎提出改进我的解决方案的建议。

【问题讨论】:

  • 这个问题很有趣,但是通过打印源代码来描述你的算法很糟糕。您需要提供解释。
  • 不过,有时候,来源最好的文档。
  • 你的算法写得很好,也足够好,没有比你更好的算法了,因为任何解决这个问题的算法都应该至少检查每个节点状态一次,并且与你的算法具有相同的顺序。
  • O (n ^ 2) 你的意思是O (r * c),对吧?
  • 对,如果n 是您的输入大小,这实际上是O(n),因为您为每个输入单元格做一件事。

标签: c algorithm time-complexity


【解决方案1】:

我认为这里的混淆在于您的算法实际上是在线性时间内运行,而不是二次时间。

当使用大 O 表示法时,n 代表输入大小。您在此处的输入只是rc,而是r * c,因为它是一个节点网格。正如您在问题中所说,您的算法在O(r * c) 中运行......因此您的算法在O(n) 中运行,这是线性时间。

在我看来,解决这个问题的任何算法都必须在最坏的情况下读取每个输入单元格一次。因此,您可以期望的最佳运行时间是O(n)。由于您的算法在 O(n) 中运行,因此在最坏的情况下,没有任何算法可以运行比您建议的算法更快的顺序。

我能想到一些巧妙的技巧。例如,如果您有一个 *s 块,在某些情况下您只能检查对角线。也就是说,如果你有

......
.****.
.****.
.****.
.****.
......

只阅读这些单元格也没关系:

......
.*.*..
..*.*.
.*.*..
..*.*.
......

除非你在最左下角有东西,在这种情况下你需要阅读最左下角的*。所以也许在某些情况下你的算法可以运行得更快,但对于最坏的情况(这是O 衡量的),它必须是O(n)

编辑:即使在您只读取一半节点的情况下,运行时间也是O(n/2),它仍然是相同的顺序(O(n))。

【讨论】:

    【解决方案2】:

    这与connected component labeling 高度相关。连接组件的数量只是标签的副产品。链接的维基百科文章中描述的算法在线性时间内起作用。

    【讨论】:

      【解决方案3】:
      1. 创建一个无向图,其中每个岛节点都连接到其相邻的岛节点。

      2. 虽然有未访问的节点:

        • 选择一个未访问过的节点,进行深度优先遍历并标记每个访问过的节点,增加 number_of_islands。
      3. 完成。

      (1) 和 (2) 都需要 O(n) 时间。

      【讨论】:

      • 这基本上是 OP 的代码所做的,除了他使用隐式图。该算法与 OP 的顺序相同。
      • 是的。但我的回答更有意识,更容易理解,更明显正确,使用正确的术语和可重复使用的组件。如果我是面试官,我会雇用我而不是 OP :-)
      • 您的答案也较慢(有更多开销),特别是如果您天真地实现它(例如,使用图形指针,因此您现在到处都有内存分配)。它并不那么简单。我宁愿在 OP 的代码中添加一条评论,说“在邻近岛屿上进行深度优先搜索”并保留它而不是重新设计整个事情
      • 正确。我的只是对面试问题的一个简单、快速、正确的答案(也可以重复用于回答许多类似的问题)
      • 是的。给了你+1,因为我确实看到了能够如此简洁地表达它的价值(而不是粘贴一堆代码)。我想我只是将“创建无向图”替换为“将数组视为无向图”。
      【解决方案4】:

      渐近而言,您的方法是最好的 O(n)。

      但是,我注意到了几件事:

      第一:

      在函数 markVisited 中,您可以按顺序检查一个单元格的邻居:

      down, right, up, left, down-right, up-left, up-right, down-left
      

      更好的顺序是:

      left, right, up-left, up, up-right, down-left, down, down-right
      

      这将在缓存中播放得更好,因为它是通过直接读取当前位置旁边的值开始的,并且在给定的行中按顺序读取。

      (请注意,从对角线开始会标记访问过更多的位置,但由于检查单元格是否被访问只是最后一次检查,因此不会节省任何时间)。

      其次:

      在卫星图像仅包含陆地的最坏情况下,您的算法将多次访问每个单元格(类似于单元格拥有的每个邻居一次)。

      这意味着您执行的数组访问次数大约是可能需要的八倍。

      我相信您可以通过或多或少地线性传递数据来解决这个问题。

      我目前正在考虑一种方法,如果可行,我会将代码添加为编辑。

      【讨论】:

        【解决方案5】:

        在没有任何关于岛屿性质的先验知识的情况下,算法无法在时间上比 O(n) 更有效,但是在内存方面可以改进您的算法。访问的数组只是多余的。这是一个快速的尝试(请原谅使用 ASCII 算法 - 不是那么可读,但编码速度更快)

        #include <stdio.h>
        #define COLS 12
        
        
        int main()
        {
            char map[][COLS] = {
                    "*..........",
                    "**........*",
                    "...........",
                    "...*.......",
                    "*........*.",
                    "..........*"               
                    };
            int rows = sizeof(map)/sizeof(map[0]);
            int i, j;  
        
            int main_count = 0;
        
            if(map[0][0] == '*') {
                main_count++;
            }
            for(j=0; j<COLS-1; j++) {
                if((map[0][j]-map[0][j+1])==4) {
                    main_count++;   
                }
            }
        
            for(i=1; i<rows; ++i){
                if(map[i][0] == '*' && (map[i][0]-map[i-1][0]-map[i-1][1])==-50) {
                    main_count++;
                }
                for(j=0; j<COLS-1; j++) {
                    if((map[i][j]-map[i][j+1])==4) {
                        if( j==COLS-2 && (map[i][j+1]-map[i-1][j]-map[i-1][j+1])==-50) {
                            main_count++;
                        }   
                        if( j!=COLS-2 && (map[i][j+1]-map[i-1][j]-map[i-1][j+1])-map[i-1][j+1]==-96) {
                            main_count++;
                        }   
                    }
                }
            }
        
            printf("Number of islands: %d\n", main_count);
        
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-29
          • 1970-01-01
          相关资源
          最近更新 更多