Leetcode每日一题-200. 岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:
11110
11010
11000
00000
输出: 1

示例 2:

输入:
11000
11000
00100
00011
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

解题思路

岛屿问题是网格类型的典型案例,主要考察DFS(深度优先搜索)和BFS(广度优先搜索)以及Union find(并查集)。

DFS写法(使用迭代和递归):遍历整个网格,如果一个位置是'1',则以该店进行深度搜索查询,计数的同时把位置'1'置为'0'(同时需要注意边界问题,防止出界)。

class Solution {
    //dfs
    private int rows;
    private int cols;
    private int count = 0;
    public int numIslands(char[][] grid) {
        
        if(grid == null || grid.length == 0) return 0;

        rows = grid.length;
        cols = grid[0].length;
        for(int i = 0 ; i < rows ; i++)
        {
            for(int j = 0 ; j < cols ; j++)
            {
                if(grid[i][j] == '0') continue;

                count++;
                dfs(grid,i,j);
            }
        }

        return count;
    }

    //每一个临界的'1'都要被重置为'0',直到遍历完为止
    private void dfs(char[][] grid, int i, int j)
    {
        if(i < 0 || j < 0 || i >= rows || j >= cols) return;

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

运算结果:执行用时:2ms,内存消耗:42MB

BFS写法(使用队列):扫描整个网格,如果位置是'1',则加入队列,并以该位置开始进行广度搜索,在广度搜索中每一个'1'都会被重置为'0',知道队列为空,搜索结束。

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

        int rows = grid.length;
        int cols = grid[0].length;
        int count = 0;

        for(int i=0;i<rows;i++)
        {
            for(int j=0;j<cols;j++)
            {
                if(grid[i][j] == '1')
                {
                    grid[i][j] = '0';
                    count++;
                    Queue<Integer> queue = new LinkedList<Integer>();
                    queue.offer(i*cols+j);
                    //广度优先搜索
                    while(!queue.isEmpty())
                    {
                        int index = queue.poll();
                        int x = index/cols;
                        int y = index%cols;

                        if(x-1>=0 &&grid[x-1][y] == '1') 
                        {
                            grid[x-1][y] = '0';
                            queue.offer((x-1)*cols+y);
                        }
                        if(x+1<rows &&grid[x+1][y] == '1')
                        {
                            grid[x+1][y] = '0';
                            queue.offer((x+1)*cols+y);
                        }
                        if(y-1>=0 &&grid[x][y-1] == '1')
                        {
                            grid[x][y-1] = '0';
                            queue.offer(x*cols+(y-1));
                        }
                        if(y+1<cols &&grid[x][y+1] == '1')
                        {
                            grid[x][y+1] = '0';
                            queue.offer(x*cols+(y+1));
                        }
                    }
                }

            }
        }
        return count;
    }
}

运算结果:执行用时:6ms,内存消耗:42.4MB

union find(并查集):并查集这种数据结构主要用于数据的分类和判断两个元素是否属于同一类别,可以借助该思想对题目中的满足条件的岛屿进行合并。

class Solution {
    
    //union find
    public int numIslands(char[][] grid) {
    
        if(grid == null || grid.length == 0) return 0;
        
        int row = grid.length;
        int col = grid[0].length;
        
        UnionFind uf = new UnionFind(grid);
        
        for(int i=0;i<row;i++)
        {
            for(int j=0;j<col;j++)
            {
                if(grid[i][j] == '1')
                {
                    grid[i][j] = '0';
                    
                    if(i-1 >= 0 && grid[i-1][j] == '1')
                    {
                        uf.union(i*col+j,(i-1)*col+j);
                    }
                    if(i+1 < row && grid[i+1][j] == '1')
                    {
                        uf.union(i*col+j,(i+1)*col+j);
                    }
                    if(j-1 >= 0 && grid[i][j-1] == '1')
                    {
                        uf.union(i*col+j,i*col+j-1);
                    }
                    if(j+1 < col && grid[i][j+1] == '1')
                    {
                        uf.union(i*col+j,i*col+j+1);
                    }
                }
            }
        }
        
        return uf.getCount();
    }
    
    class UnionFind
    {
        private int[] parent;
        private int[] rank;
        private int count = 0;
        
        public UnionFind(char[][] grid)
        {

            int row = grid.length;
            int col = grid[0].length;
            
            parent = new int[row*col];
            rank = new int[row*col];
            
            for(int i=0;i<row;i++)
            {
                for(int j=0;j<col;j++)
                {
                    if(grid[i][j] == '1')
                    {
                        count++;
                        parent[i*col+j] = i*col+j;  //每个岛屿‘1’都是一个集合,剩下的就是集合的合并
                    }
                    
                    rank[i*col+j] = 0;
                }
            }
        }
        
        //递归找到root,方便后面union方法中x和root直接建立联系,压缩路径
        public int find(int x)
        {
            if(x != parent[x]) parent[x] = find(parent[x]);
            return parent[x];
        }
        
        public void union(int x,int y)
        {
            int rootX = find(x);
            int rootY = find(y);
            
            if(rootX != rootY)
            {
                //层级小的归并到层级大的集合上
                if(rank[rootX] < rank[rootY])
                {
                    parent[rootX] = rootY; 
                }else if(rank[rootX] > rank[rootY])
                {
                    parent[rootY] = rootX; 
                }else
                {
                    parent[rootY] = rootX; 
                    rank[rootX] += 1; 
                }
                
                //去除重复的关联岛屿
                count--;
            }
        }
        
        public int getCount()
        {
            return count;
        }
        
        
    }
}

执行结果:执行用时 : 6 ms 内存消耗 :42 MB

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,576评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,515评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,017评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,626评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,625评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,255评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,825评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,729评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,271评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,363评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,498评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,183评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,867评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,338评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,458评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,906评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,507评论 2 359

推荐阅读更多精彩内容