A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand operation which turns the water at position (row, col) into a land. Given a list of positions to operate, count the number of islands after each addLand operation. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example:
Input: m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]
Output: [1,1,2,3]
Explanation:
Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 represents land).
0 0 0
0 0 0
0 0 0
Operation #1: addLand(0, 0) turns the water at grid[0][0] into a land.
1 0 0
0 0 0 Number of islands = 1
0 0 0
Operation #2: addLand(0, 1) turns the water at grid[0][1] into a land.
1 1 0
0 0 0 Number of islands = 1
0 0 0
Operation #3: addLand(1, 2) turns the water at grid[1][2] into a land.
1 1 0
0 0 1 Number of islands = 2
0 0 0
Operation #4: addLand(2, 1) turns the water at grid[2][1] into a land.
1 1 0
0 0 1 Number of islands = 3
0 1 0
Follow up: Can you do it in time complexity O(k log mn), where k is the length of the positions?
Basic Idea:
每次操作在图中增加一个点,要求记录每次操作完成之后独立 island 的个数。可以理解为每次操作后求图中所有联通量的个数,自然就想到了并查集(Union Find or Disjoint Set)。
class Solution {
int[] ids;
int[] ranks;
int count = 0;
void makeSet(int size) {
ids = new int[size];
Arrays.fill(ids, -1);
ranks = new int[size];
}
int find(int id) {
if (ids[id] == id) return id;
while (ids[id] != id) {
id = ids[id];
}
return id;
}
void union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx == rooty) return;
if (ranks[rootx] < ranks[rooty]) ids[rootx] = rooty;
else if (ranks[rooty] < ranks[rootx]) ids[rooty] = rootx;
else {
ids[rootx] = rooty;
ranks[rooty]++;
}
count--;
}
public List<Integer> numIslands2(int m, int n, int[][] positions) {
int[] dr = new int[]{-1, 0, 1, 0};
int[] dc = new int[]{0, -1, 0, 1};
List<Integer> res = new ArrayList<>();
makeSet(m * n);
for (int[] coord : positions) {
int r = coord[0], c = coord[1];
int k = r * n + c;
// 先将 id=k 的点加入,算作一个island,count++
if (ids[k] != -1) continue;
ids[k] = k;
count++;
// 查看四边,如果与其他岛相连,则union,count自然减少
for (int i = 0; i < 4; ++i) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr < 0 || nr >= m || nc < 0 || nc >= n) continue;
int nk = nr * n + nc;
if (ids[nk] != -1) union(nk, k);
}
res.add(count);
}
return res;
}
}
class Solution {
unordered_map<int, int> ids;
unordered_map<int, int> ranks;
int count = 0;
int find(int id) {
if (! ids.count(id)) return -1;
if (ids[id] == id) return id;
while (ids[id] != id) {
id = ids[id];
}
return id;
}
void _union(int x, int y) {
int rootx = find(x);
int rooty = find(y);
if (rootx == -1 || rooty == -1) exit(1);
if (rootx == rooty) return;
if (ranks[rootx] < ranks[rooty]) ids[rootx] = rooty;
else if (ranks[rootx] > ranks[rooty]) ids[rooty] = rootx;
else {
ids[rootx] = rooty;
++ranks[rooty];
}
--count;
}
public:
vector<int> numIslands2(int m, int n, vector<pair<int, int>>& positions) {
int dr[4] = {-1, 0, 1, 0};
int dc[4] = {0, -1, 0, 1};
vector<int> res;
for (auto& coord : positions) {
int r = coord.first, c = coord.second;
int k = r * n + c;
if (ids.count(k)) continue;
ids[k] = k;
++count;
for (int i = 0; i < 4; ++i) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr < 0 || nr >= m || nc < 0 || nc >= n) continue;
int nk = nr * n + nc;
if (ids.count(nk)) _union(nk, k);
}
res.push_back(count);
}
return res;
}
};