和左神学算法,记录一下。
题目
链接: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题就只能用并查集实现了。