零 標題:演算法(leetcode,附思維導圖 + 全部解法)300題之(200)島嶼數量
一 題目描述
二 解法總覽(思維導圖)
三 全部解法
1 方案1
1)程式碼:
// 方案1 “自己。模擬 - 標記法”。
// 思路:
// 1)狀態初始化:m = grid.length, n = grid[0].length; 。
// tempMap = new Map(), resMap = getMapByGrid(), resCount = 0; 。
// 2)核心:迴圈處理 —— 條件為 存在未被訪問過的陸地 。
// 3)返回結果 resCount 。
var numIslands = function(grid) {
const getMapByGrid = () => {
let resMap = new Map();
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === '1') {
const tempVal = `${i}#${j}`;
resMap.set(tempVal, 1);
}
}
}
return resMap;
};
// 1)狀態初始化:m = grid.length, n = grid[0].length; 。
// tempMap = new Map(), resMap = getMapByGrid(), resCount = 0; 。
const m = grid.length,
n = grid[0].length;
let // tempMap:目前已被訪問過的陸地。
tempMap = new Map(),
resMap = getMapByGrid(),
resCount = 0;
// 2)核心:迴圈處理 —— 條件為 存在未被訪問過的陸地 。
while (resMap.size !== 0) {
for (const [key, val] of resMap) {
if (!tempMap.has(key)) {
tempMap.set(key, 1);
let tempQueue = [key];
while (tempQueue.length !== 0) {
const key = tempQueue.shift(),
[tempI, tempJ] = key.split('#').map(v => parseInt(v));
// 標記為已被訪問。
resMap.delete(key);
// 4個方向的訪問。
// 上
if (tempI - 1 >= 0 && grid[tempI - 1][tempJ] === '1') {
const key = `${tempI - 1}#${tempJ}`;
if (!tempMap.has(key)) {
tempQueue.push(key);
tempMap.set(key, 1);
}
}
// 下
if (tempI + 1 < m && grid[tempI + 1][tempJ] === '1') {
const key = `${tempI + 1}#${tempJ}`;
if (!tempMap.has(key)) {
tempQueue.push(key);
tempMap.set(key, 1);
}
}
// 左
if (tempJ - 1 >= 0 && grid[tempI][tempJ - 1] === '1') {
const key = `${tempI}#${tempJ - 1}`;
if (!tempMap.has(key)) {
tempQueue.push(key);
tempMap.set(key, 1);
}
}
// 右
if (tempJ + 1 < n && grid[tempI][tempJ + 1] === '1') {
const key = `${tempI}#${tempJ + 1}`;
if (!tempMap.has(key)) {
tempQueue.push(key);
tempMap.set(key, 1);
}
}
}
// 當前島嶼無法再次連線到任何陸地了。
resCount++;
}
break;
}
}
// 3)返回結果 resCount 。
return resCount;
};
2 方案2
1)程式碼:
// 方案2 “自己。深度優先搜尋法”。
// 參考:
// 1)https://leetcode.cn/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
// 思路:
// 1)狀態初始化:m = grid.length, n = grid[0].length; 。
// resCount = 0;
// 2)核心:遍歷 grid 。
// 2.1)若 當前的格子為 陸地,則 島嶼數量增加1 並 執行深度搜尋函式 —— dfs(i, j) 。
// 3)返回結果 resCount 。
var numIslands = function(grid) {
// 深度搜尋(實現:遞迴)
const dfs = (curI = 0, curJ = 0) => {
// 1)遞迴出口:其實被隱藏起來 —— 當座標超出格子範圍 或 座標不符合條件時。
// 2)遞迴主體。
// 2.1)當前陸地置為 水 。
grid[curI][curJ] = '0';
// 2.2)上下左右,四個方向執行深度搜尋。
if (curI - 1 >= 0 && grid[curI - 1][curJ] === '1') {
dfs(curI - 1, curJ);
}
if (curI + 1 < m && grid[curI + 1][curJ] === '1') {
dfs(curI + 1, curJ);
}
if (curJ - 1 >= 0 && grid[curI][curJ - 1] === '1') {
dfs(curI, curJ - 1);
}
if (curJ + 1 < n && grid[curI][curJ + 1] === '1') {
dfs(curI, curJ + 1);
}
};
// 1)狀態初始化:m = grid.length, n = grid[0].length; 。
// resCount = 0;
const m = grid.length,
n = grid[0].length;
let resCount = 0;
// 2)核心:遍歷 grid 。
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
// 2.1)若 當前的格子為 陸地,則 島嶼數量增加1 並 執行深度搜尋函式 —— dfs(i, j) 。
if (grid[i][j] === '1') {
resCount++;
dfs(i, j);
}
}
}
// 3)返回結果 resCount 。
return resCount;
};
3 方案3
1)程式碼:
// 方案3 “廣度優先搜尋法(本質:跟方案1差不多)”。
// 參考:
// 1)https://leetcode.cn/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
// 思路:
// 1)狀態初始化:m = grid.length, n = grid[0].length;
// resCount = 0; 。
// 2)核心:2層迴圈的遍歷。
// 2.1)若 當前位置為 陸地 ,特殊處理,
// 並 進行 廣度優先(使用佇列 queue )的遍歷處理。
// 3)返回結果 resCount 。
var numIslands = function(grid) {
// 1)狀態初始化:m = grid.length, n = grid[0].length;
// resCount = 0; 。
const m = grid.length,
n = grid[0].length;
let resCount = 0;
// 2)核心:2層迴圈的遍歷。
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
// 2.1)若 當前位置為 陸地 ,特殊處理,
// 並 進行 廣度優先(使用佇列 queue )的遍歷處理。
if (grid[i][j] === '1') {
grid[i][j] = '0';
resCount++;
let queue = [[i, j]];
while (queue.length !== 0) {
const [tempI, tempJ] = queue.shift();
// 上下左右,4個方向。
if (tempI - 1 >= 0 &&grid[tempI - 1][tempJ] === '1') {
queue.push([tempI - 1, tempJ]);
grid[tempI - 1][tempJ] = '0';
}
if (tempI + 1 < m &&grid[tempI + 1][tempJ] === '1') {
queue.push([tempI + 1, tempJ]);
grid[tempI + 1][tempJ] = '0';
}
if (tempJ - 1 >= 0 &&grid[tempI][tempJ - 1] === '1') {
queue.push([tempI, tempJ - 1]);
grid[tempI][tempJ - 1] = '0';
}
if (tempJ + 1 < n &&grid[tempI][tempJ + 1] === '1') {
queue.push([tempI, tempJ + 1]);
grid[tempI][tempJ + 1] = '0';
}
}
}
}
}
// 3)返回結果 resCount 。
return resCount;
}
4 方案4
1)程式碼:
// 方案4 “並查集法”。
// 參考:
// 1)https://leetcode.cn/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/
// 注:有問題,通過 12 / 49 。TODO:排查問題 && 重新手撕。
var numIslands = function(grid) {
class Unionfind {
constructor() {
let count = 0,
parent = [],
rank = [];
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
const tempIndex = n * i + j;
if (grid[i][j] === '1') {
parent[tempIndex] = tempIndex;
count++;
}
// ?
rank[tempIndex] = 0;
}
}
this.count = count;
this.parent = parent;
this.rank = rank;
}
find(index) {
const {parent} = this;
if (parent[index] !== index) {
// 找到該座標最開始的“祖先”?
parent[index] = this.find(parent[index]);
}
this.parent = parent;
return parent[index];
}
union(index_1, index_2) {
let {rank, parent, count} = this;
const root_1 = this.find(index_1),
root_2 = this.find(index_2);
if (root_1 !== root_2) {
if (rank[root_1] > rank[root_2]) {
parent[root_2] = root_1;
}
else if (rank[root_1] < rank[root_2]) {
parent[root_1] = root_2;
}
else {
parent[root_2] = root_1;
// ?
rank[root_1] += 1;
}
count--;
}
this.count = count;
this.parent = parent;
this.rank = rank;
}
getCount() {
const {count} = this;
return count;
}
}
const m = grid.length,
n = grid[0].length;
let unionfind = new Unionfind();
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === '1') {
grid[i][j] = '0';
}
// 上下左右,4個方向。
const tempIndex = n * i + j;
if (i - 1 >= 0 && grid[i - 1][j] === '1') {
unionfind.union(tempIndex, n * (i - 1) + j);
}
if (i + 1 < m && grid[i + 1][j] === '1') {
unionfind.union(tempIndex, n * (i + 1) + j);
}
if (j - 1 >= 0 && grid[i][j - 1] === '1') {
unionfind.union(tempIndex, n * i + (j - 1));
}
if (j + 1 < n && grid[i][j + 1] === '1') {
unionfind.union(tempIndex, n * i + (j + 1));
}
}
}
return unionfind.getCount();
}
四 資源分享 & 更多
1 歷史文章 - 總覽
2 博主簡介
碼農三少 ,一個致力於編寫 極簡、但齊全題解(演算法) 的博主。
專注於 一題多解、結構化思維 ,歡迎一起刷穿 LeetCode ~