897. Island City
Given a matrix of size n x m, the elements in the matrix are 0、1、2. 0 for the sea, 1 for the island, and 2 for the city on the island(You can assume that 2 is built on 1, ie 2 also represents the island).
If two 1 are adjacent, then these two 1 belong to the same island. Find the number of islands with at least one city.
Example
Example1
Input:
[
[1,1,0,0,0],
[0,1,0,0,1],
[0,0,0,1,1],
[0,0,0,0,0],
[0,0,0,0,1]
]
Output: 0
Explanation:
There are 3 islands, but none of them contain cities.
Example2
Input:
[
[1,1,0,0,0],
[0,1,0,0,1],
[0,0,2,1,2],
[0,0,0,0,0],
[0,0,0,0,2]
]
Output: 2
Explanation:
There are 3 islands, and two of them have cities.
Notice
We only consider up, down, left and right as adjacent.
n <= 100,m <= 100.
You can assume that the four sides of the matrix are surrounded by the sea.
解法1:BFS
注意 grid[curNode.first][curNode.second] = 0 要在for循环前面执行,不然for 循环里面又碰到它是1或2的时候就出错了。
class Solution {
public:
/**
* @param grid: an integer matrix
* @return: an integer
*/
int numIslandCities(vector<vector<int>> &grid) {
int nRow = grid.size();
int nCol = grid[0].size();
int result = 0;
for (int i = 0; i < nRow; ++i) {
for (int j = 0; j < nCol; ++j) {
if (grid[i][j] == 0) continue;
if (bfs(grid, i, j)) result++;
}
}
return result;
}
private:
//return true if it has island and the island has city
bool bfs(vector<vector<int>> &grid, int x, int y) {
int nRow = grid.size();
int nCol = grid[0].size();
bool result = false;
vector<int> dx = {0, 0, 1, -1};
vector<int> dy = {1, -1, 0, 0};
queue<pair<int, int>> q;
q.push({x, y});
while(!q.empty()) {
auto curNode = q.front();
q.pop();
if (grid[curNode.first][curNode.second] == 2) result = true;
grid[curNode.first][curNode.second] = 0;
for (int i = 0; i < 4; ++i) {
int newX = curNode.first + dx[i];
int newY = curNode.second + dy[i];
if (newX < 0 || newX >= grid.size() || newY < 0 || newY >= grid[0].size() || grid[newX][newY] == 0) continue;
q.push({newX, newY});
}
}
return result;
}
};
解法2:DFS
class Solution {
public:
/**
* @param grid: an integer matrix
* @return: an integer
*/
int numIslandCities(vector<vector<int>> &grid) {
int nRow = grid.size();
int nCol = grid[0].size();
int result = 0;
for (int i = 0; i < nRow; ++i) {
for (int j = 0; j < nCol; ++j) {
int count = 0;
if (grid[i][j] != 0) {
dfs(grid, i, j, count);
if (count > 0) result++;
}
}
}
return result;
}
private:
void dfs(vector<vector<int>> &grid, int x, int y, int & count) {
int nRow = grid.size();
int nCol = grid[0].size();
bool result = false;
if (x < 0 || x >= nRow || y < 0 || y >= nCol || grid[x][y] == 0)
return;
if (grid[x][y] == 2) {
count++;
}
grid[x][y] = 0;
vector<int> dx = {0, 0, 1, -1};
vector<int> dy = {1, -1, 0, 0};
for (int i = 0; i < 4; ++i) {
int newX = x + dx[i];
int newY = y + dy[i];
dfs(grid, newX, newY, count);
}
}
};
解法3:Union-Find
注意,并查集算法并不需要上下左右4个方向,只需要下和右两个方向就可以了。为什么呢?因为union算法决定了连在一起的都是一个连通块,所以不需要再往左和上再搞一次了。
而BFS和DFS则必须要上下左右4个方向处理。为什么呢?我们看下面的矩阵M:
[[1,1,0,0,0],
[0,1,0,0,1],
[0,0,2,1,2],
[0,0,0,0,0],
[0,0,0,0,2]]
M[1][4]=1,此时如果只往下和右方向走就只会处理M[2][4],而不会处理与之相连的M[2][2]和M[2][3]。
而并查集算法则会把M[1][4]和M[2][4]的祖先合成一个,到时候再处理M[2][2]和M[2][3]的时候又把其祖先合成一个,处理M[2][3]和M[2][4]的时候又把其祖先合成一个。这样,大家就都是一个连通块了。
注意,并查集并不需要把grid[][]元素改成0,而BFS/DFS可以这样做,从而省掉一个visited数组。
class Solution {
public:
/**
* @param grid: an integer matrix
* @return: an integer
*/
int numIslandCities(vector<vector<int>> &grid) {
int m = grid.size();
int n = grid[0].size();
father.resize(m * n);
hasCity.resize(m * n);
// initialize
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
father[i * n + j] = i * n + j;
hasCity[i * n + j] = grid[i][j] == 2 ? true : false;
if (grid[i][j] == 2) count++;
}
}
//union-find
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] != 0) {
if (i + 1 >= 0 && i + 1 < m && j >= 0 && j < n && grid[i + 1][j] != 0) {
add(i * n + j, (i + 1) * n + j);
}
if (i >= 0 && i < m && j + 1 >= 0 && j + 1 < n && grid[i][j + 1] != 0) {
add(i * n + j, i * n + j + 1);
}
}
}
}
return count;
}
private:
vector<int> father;
vector<bool> hasCity;
int count = 0;
int find(int x) {
if (father[x] == x) return x;
father[x] = find(father[x]);
return father[x];
}
void add(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if (root_a != root_b) {
father[root_a] = root_b;
if (hasCity[root_a] && hasCity[root_b]) count--;
if (hasCity[a] || hasCity[b]) {
hasCity[a] = hasCity[b] = true;
}
}
}
};