种子填充问题

两种连通区域
种子填充问题_第1张图片
四连通区域:从区域内一点出发,可通过上、下、左、右四个方向的移动组合,在不越出区域的前提下,能到达区域内的任意像素

八连通区域:从区域内每一像素出发,可通过八个方向,即上、下、左、右、左上、右上、左下、右下移动的组合,在不越出区域的前提下,能到达区域内的任意像素。
种子填充问题_第2张图片

基本原理

从多边形区域内部的某一像素点(称为种子)开始,由此出发找到区域内的其它所有像素。

算法的执行过程:

从(x,y)开始,先检测该点的颜色,若它与边界色和填充色均不相同,则用填充色填充该点。然后检测相邻位置,以确定它们是否是边界色和填充色,若不是,则填充该相邻点。直到检测完区域边界范围内的所有像素为止。

void ZhongZiTC4 (int seedx, int seedy, int fcolor, int bcolor)  
{      
     int current = getpixel (seedx, seedy);  
     if ((current != bcolor) && (current != fcolor))  
     {   putpixel (seedx, seedy, fcolor);  
     ZhongZiTC4 (seedx+1, seedy, fcolor, bcolor);  //右  
     ZhongZiTC4 (seedx–1, seedy, fcolor, bcolor);  //左  
     ZhongZiTC4 (seedx, seedy+1, fcolor, bcolor);  //上  
     ZhongZiTC4 (seedx, seedy–1, fcolor, bcolor);  //下  
     }  
}   

LeetCode 200. Number of Islands (岛屿的个数)

给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

示例 1:

输入:
11110
11010
11000
00000

输出: 1

示例 2:

输入:
11000
11000
00100
00011

输出: 3

题解:
1、DFS
注意开始先判断直接返回的情况特殊输入情况;
遍历所有点,遇到一个‘1’ 就count++,然后开始深搜,标记与该点连接的所有点为‘0’。

class Solution {
    int count;
    char[][] grid;
    int m;//行
    int n;//列
    public int numIslands(char[][] gr) {
        if(gr==null) return 0;
        m=gr.length;
        if(m==0)return 0;
        n=gr[0].length;
        if(n==0) return 0;

        count=0;
        grid=gr;
        for(int i=0;ifor(int j=0;jif(grid[i][j]=='0') continue;
                count++;
                myDFS(i,j);
            }
        }

        return count;
    }
    private void myDFS(int i, int j) {
        if(i<0||i>=m||j<0||j>=n){//递归退出
            return;
        }

        if(grid[i][j]=='1'){
            grid[i][j]='0';
            myDFS(i-1,j);
            myDFS(i+1,j);
            myDFS(i,j-1);
            myDFS(i,j+1);
        }
    }
}

2、BFS
耗时比DFS长
如果按照以往,先放入队列,出队时再访问,会超时,改为入队前先访问,再入队,通过。可能是因为,出队时再访问,会调用封装对象的 coor.x和coor.y 属性,增加耗时。

class Solution {
    int count;
    char[][] grid;
    int m;// 行
    int n;// 列
    int[][] fx = new int[][] { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; // 表示移动的位置,下,上、右、左

    public int numIslands(char[][] gr) {
        if (gr == null || gr.length == 0 || gr[0].length == 0)
            return 0;

        m = gr.length;
        n = gr[0].length;
        count = 0;
        grid = gr;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '0')
                    continue;
                count++;
                markByBFS(i, j);// 用宽度优先搜索标记与其相连的点
            }
        }

        return count;
    }

    private void markByBFS(int x, int y) {
        Queue queue = new LinkedList<>();// 宽度优先遍历,需要用到队列,将每个点封装成一个Coordinate类,便于保存横轴坐标值
        grid[x][y] = '0'; //先访问在入队
        queue.add(new Coordinate(x, y));//先入队

        while (!queue.isEmpty()) {
            Coordinate coor = queue.poll();

            for (int i = 0; i < 4; i++) {
                int xx = coor.x + fx[i][0];
                int yy = coor.y + fx[i][1];
                if (xx < m && xx >= 0 && yy < n && yy >= 0) {//没有越界
                    if(grid[xx][yy]=='1'){//没被访问过
                        grid[xx][yy] = '0'; //访问之
                        queue.add(new Coordinate(xx, yy));
                    }
                }
            }
        }
    }
}

class Coordinate {
    int x, y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

LeetCode130. Surrounded Regions(被围绕的区域)

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。

找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例:

X X X X
X O O X
X X O X
X O X X

运行你的函数后,矩阵变为:

X X X X
X X X X
X X X X
X O X X

解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

题解:
本来应该从非边界上的 O 开始遍历,但需要考虑与边界字符的连接问题,较为复杂。
转换思维,遍历四周边界,并开始深搜,将与边界上O相连的O,都标记为另一种标记,如 *
最后遍历所有位置,将* 变为O,其他都变为 X

class Solution {
    char[][] grid;
    int m;// 行
    int n;// 列

    public void solve(char[][] board) {
        if (board == null || board.length == 0 || board[0].length == 0)
            return ;

        m = board.length;
        n = board[0].length;
        grid = board;
        for (int i = 0; i < n; i++) {
            if (grid[0][i] == 'O'){//遍历第一行
                markByDFS(0, i);
            }
            if(grid[m-1][i]=='O'){//遍历最后一行
                markByDFS(m-1,i);
            }
        }
        for(int i=1;i1;i++){
            if(grid[i][0] == 'O'){//遍历第一列
                markByDFS(i, 0);
            }
            if(grid[i][n-1] == 'O'){//遍历最后一列
                markByDFS(i, n-1);
            }
        }
        for(int i=0;ifor(int j=0;jif(grid[i][j]=='*'){
                    grid[i][j]='O';
                }else{
                    grid[i][j]='X';
                }
            }
        }
    }

    private void markByDFS(int x, int y) {
        if(x>=m||x<0||y>=n||y<0){
            return;
        }
        if(grid[x][y]=='O'){
            grid[x][y]='*';
            markByDFS(x-1,y);
            markByDFS(x+1,y);
            markByDFS(x,y-1);
            markByDFS(x,y+1);
        }
    }
}

你可能感兴趣的:(OJ)