用并查集解决leetcode200题-岛屿数量

和左神学算法,记录一下。

题目

链接:https://leetcode-cn.com/problems/number-of-islands/

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

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

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

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

解决方案一 递归遍历:

用递归的方式,最多遍历4*n次,时间复杂度o(n),额外空间复杂度o(1),实际时间是3ms

public static int numIslands3(char[][] grid) {
        int size = 0;
        for (int i = 0; i < grid.length; i ++) {
            for (int j = 0; j < grid[i].length; j ++) {
                if (grid[i][j] == '1') {
                    size ++;
                    connect(grid, i, j);
                }
            }
        }
        return size;
    }

    private static void connect(char[][] matrix, int i, int j) {
        if (j < 0 || i < 0 || i >= matrix.length || j >= matrix[i].length) {
            return ;
        }
        if (matrix[i][j] == '0' || matrix[i][j] == '2') {
            return ;
        }
        matrix[i][j] = '2';
        connect(matrix, i, j+1);
        connect(matrix, i, j-1);
        connect(matrix, i-1, j);
        connect(matrix, i+1, j);
    }

方案二、用hashmap实现并查集

实际时间是41ms,时间复杂度是o(n),慢是因为哈希的常数时间操作会多一些,额外空间是O(n),


    public static int numIslands1(char[][] grid) {
        if (grid == null) {
            return 0;
        }
        int rowSize = grid.length;
        int colSize = grid[0].length;
        Bot[][] botArr = new Bot[rowSize][colSize];
        List<Bot> list = new ArrayList<>();
        for (int i= 0; i < grid.length; i ++) {
            for (int j = 0; j < grid[i].length; j ++) {
                if (grid[i][j] == '1') {
                    botArr[i][j] = new Bot();
                    list.add(botArr[i][j]);
                }
            }
        }
        UnionFind unionFind = new UnionFind(list);
        for (int i= 0; i < grid.length; i ++) {
            for (int j = 0; j < grid[i].length; j ++) {
                if (grid[i][j] == '1') {
                    if (i > 0 && botArr[i-1][j] != null) {
                        unionFind.union(botArr[i][j], botArr[i-1][j]);
                    }
                    if (j > 0 && botArr[i][j-1] != null) {
                        unionFind.union(botArr[i][j], botArr[i][j-1]);
                    }
                }
            }
        }
        return unionFind.size();
    }

    public static class Bot {

    }

    public static class UnionFind {
        HashMap<Bot, Bot> parentMap;
        HashMap<Bot, Integer> sizeMap;
        public UnionFind(List<Bot> list) {
            parentMap = new HashMap<>();
            sizeMap = new HashMap<>();
            for (Bot bot : list) {
                parentMap.put(bot, bot);
                sizeMap.put(bot, 1);
            }
        }

        public Bot findFather(Bot bot) {
            Stack<Bot> stack = new Stack<>();
            while (parentMap.get(bot) != bot) {
                stack.add(bot);
                bot = parentMap.get(bot);
            }
            while (!stack.isEmpty()) {
                parentMap.put(stack.pop(), bot);
            }
            return bot;
        }

        public void union(Bot a, Bot b) {
            Bot aP = findFather(a);
            Bot bP = findFather(b);
            if (aP != bP) {
                int bSize = sizeMap.get(bP);
                int aSize = sizeMap.get(aP);
                Bot small = (aSize > bSize) ? bP : aP;
                Bot big = (aSize > bSize) ? aP : bP;
                parentMap.put(small, big);
                sizeMap.put(big, (sizeMap.get(small) + sizeMap.get(big)));
                sizeMap.remove(small);
            }
        }

        public int size() {
            return sizeMap.size();
        }
    }

方案三、用数组实现并查集

实际时间是6ms,时间复杂度是o(n),数组的常数时间操作会比哈希的常数值低,但是还是不及方案一,额外空间是O(n),

    public static int numIslands2(char[][] grid) {
        if (grid == null) {
            return 0;
        }
        int row = grid.length;
        int col = grid[0].length;
        int size = row * col;
        int[] arr = new int[size];
        for (int i = 0; i < grid.length; i ++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == '1') {
                    arr[index(i, j, col)] = 1;
                }
            }
        }
        UnionFind2 unionFind2 = new UnionFind2(arr);
        for (int i = 0; i < grid.length; i ++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == '1') {
                    if (i > 0 && grid[i-1][j] == '1') {
                        unionFind2.union(index(i, j, col), index(i-1, j, col));
                    }
                    if (j > 0 && grid[i][j-1] == '1') {
                        unionFind2.union(index(i, j, col), index(i, j-1, col));
                    }
                }
            }
        }
        return unionFind2.size();
    }

    public static int index(int i, int j, int col) {
        return (i * col + j);
    }

    public static class UnionFind2 {
        int[] parents;
        int[] sizes;
        int[] helps;
        int sets;
        public UnionFind2(int[] arr) {
            parents = new int[arr.length];
            sizes = new int[arr.length];
            helps = new int[arr.length];
            sets = 0;
            for (int i = 0; i < arr.length; i ++) {
                if (arr[i] != 0) {
                    parents[i] = i;
                    sizes[i] = 1;
                    sets++;
                }
            }
        }

        public int find(int i) {
            int index = 0;
            while (parents[i] != i) {
                helps[index++] = i;
                i = parents[i];
            }
            while (index > 0) {
                parents[helps[--index]] = i;
            }
            return i;
        }

        public void union(int a, int b) {
            int ap = find(a);
            int bp = find(b);
            if (ap != bp) {
                int sizea = sizes[ap];
                int sizeb = sizes[bp];
                int small = sizea > sizeb ? bp : ap;
                int big = sizea > sizeb ? ap : bp;
                parents[small] = big;
                sizes[big] = sizes[small] + sizes[big];
                sizes[small] = 0;
                sets--;
            }
        }

        public int size() {
            return sets;
        }
    }

总结

方案一最快,空间复杂度也最低。

如果是用并查集,方案三用数组实现会比方案二用hashmap实现性能更好。因为常数值低。

这一题可以用方案一实现,但是岛屿数量的升级版,leetcode 305题就只能用并查集实现了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值