1.題目描述
給定一個由 '1'
(陸地)和 '0'
(水)組成的的二維網格,計算島嶼的數量。一個島被水包圍,並且它是通過水平方向或垂直方向上相鄰的陸地連接而成的。你可以假設網格的四個邊均被水包圍。
示例 1:
輸入: 11110 11010 11000 00000 輸出: 1
示例 2:
輸入: 11000 11000 00100 00011 輸出: 3
2.解題思路
算法核心框架:深度搜索+遞歸
-
- 深度搜索:整個島嶼是一個“二維矩陣”(vector<vectro<char>>),使用深度優先搜索遍歷一遍整個矩陣;
- 遞歸:當掃描到'1'時,調用infect()函數,將該 ‘1’ 上下左右相鄰的位置都感染為 ‘2’,遞歸調用infect()的結果是從起始的 ‘1’ 開始而相連的一片‘1’都被感染為 ‘2’;
3.提交代碼
class Solution { public: int numIslands(vector<vector<char>>& grid) { //行空 或 列空 if(grid.empty() || grid[0].empty()){ return 0; } int N = grid.size();//grid網格的列長 int M = grid[0].size();//grid網格的行長 int res = 0; //深度優先搜索 for(int i = 0; i < N; ++i){ for(int j = 0; j < M; ++j){ if(grid[i][j] == '1'){ ++res;//只計數作為起始感染點的1的個數,即為島嶼個數 infect(grid,i,j,N,M); } } } return res; } void infect(vector<vector<char>>& grid,int i,int j,int N,int M){ if(i<0 || i>=N || j<0 || j>= M || grid[i][j] != '1'){ return; } grid[i][j] = '2'; infect(grid,i+1,j,N,M); infect(grid,i-1,j,N,M); infect(grid,i,j-1,N,M); infect(grid,i,j+1,N,M); } };
4.補充知識——並查集
並查集的結構本質是多叉樹,是樹的一種應用。對於並查集的具體結構,不同的教材版本有不同的定義,我這里介紹的文末所列參考資料中定義。
-
- 初始時,每個單一元素是一個集合,並且各自的父指針指向自己,各個集合的“代表結點”是其自身;
- 合並時,總是將相對較小的集合合並到大集合中,合並操作只需要將小集合的代表結點的父指針指向大集合的代表結點;
- 查找時,兩個元素的代表結點相同,表示在同一個集合。
初始時:每個元素的父指針指向自己 1->1 2->2 3->3 4->4 5->5 合並1,2,得到set1: 2的父指針指向1 2——>1->1 合並3,4,5,得到set2: 集合相等,可以任意選擇哪個集合合並到哪個集合,這里3和4並入5 3——>5->5 4——>5->5 合並set1和set2:小集合set1的代表結點的父指針指向set2的代表結點 2——>1——>5->5 3——>5->5 4——>5->5 查找元素2和4是否在同一個集合: 2往上查找得到代表結點為5,4往上查找得到代表結點為5,所以,2和4在同一個集合中。
並查集是一種簡單的集合表示,主要的作用有兩個:
-
- 快速的,檢查兩個元素是否屬於同一個集合,isSameSet(A,B);
- 快速的,將兩個元素各自所在的不同集合合並在一起,Union(A,B),指的合並A所在的集合set1和B所在的集合set2;
另外,特別注意一點:並查集要求一次性獲得所有的處理數據樣本,不支持處理動態的數據流。
5.擴展解法——多任務並行計算
假設題目給的數據非常多,也就是所要查找的矩陣相當大,這種情況下,我們該怎么辦?很明顯,上面的解法只能在單機上運行,效率有上限。
因此,我們需要采用多任務處理的“並行計算”的思路(讓你在面試官面前脫穎而出),將大的原始矩陣分割成多個小的矩陣,分布在多台機器上進行計算,最后合並每台機器的島嶼數量,並去重,得到最終的島嶼總數。
#問題的難點:邊界信息該如何合並?
01111 | 11110 11000 | 00000 11000 | 00000 01111 | 11110 島數1 島數2 合並結果:島數1 (怎么得到?)
#解決思路:需要保存的關鍵信息有兩個:
- (1)每個分矩陣的島嶼個數;
- (2)記錄邊界點的感染中心——需要使用到並查集的結構,快速查找兩個‘1’是否屬於同一個集合(相同的感染中心)。
舉例:划分為2個矩陣 矩陣1 矩陣2 01111 | 11110 01111 | 10000 00000 | 10001 01111 | 10001 00001 | 00111 (1)矩陣1的島嶼個數為2,矩陣2的島嶼個數為2 (2)邊界點的感染中心:為了易於區別不同的'1',設紅、藍、黑、綠色島嶼的感染中心(感染起始點的那個'1')的分別為A、B、C、D 0A111 | B1110 01111 | 10000 00000 | 1000D 0C111 | 10001 00001 | 00111 第1行邊界的兩個1,感染中心分別為A和B,不相同,島嶼數減一,並且將感染中心為B的島嶼上的1的感染中心全部置為A; 第2行邊界的兩個1,感染中心分別為A和A(島嶼B的感染中心在上一步更新為A了),相同,跳過; 第3行邊界的兩個不全為1,跳過; 第4行邊界的兩個1,感染中心分別為C和A,不相同,島嶼數減一,並且將感染中心為C的島嶼上的1的感染中心全部置為A; 第5行邊界的兩個不全為1,跳過; 島嶼總數:2+2 -1 -1 = 2
兩個分矩陣的合並處理解決了,四個或八個矩陣的合並同理,只是需要重復的步驟多了。
參考資料:
1.《數據結構考研復習指導》--王道單科書 www.cskaoyan.com
2.《算法初級班視頻 》--牛客網大牛 · 左程雲