avatarChih-Yu Lin

总结

本文主要介绍了如何通过LeetCode上的“岛屿数量”问题来学习深度优先搜索(DFS)算法。

摘要

文章首先提醒读者,如果觉得内容有帮助,可以点击页面底部的“SHOW EMBED”并给予五次点赞,以支持作者继续创作。接着,文章详细阐述了LeetCode的“0200 Number of Islands”问题,该问题要求在一个由‘1’(陆地)和‘0’(水)组成的二维网格中,计算岛屿的数量。岛屿是由水包围的连续陆地,可以通过相邻的水平或垂直位置连接。文章通过示例输入和输出来说明问题。

文章进一步分析了解题方法,提出了使用DFS算法来确定岛屿的方法。DFS算法需要遍历每个点,并通过标记已访问的区域来避免重复计算。文章详细描述了DFS算法的步骤,包括初始化一个visited数组,检查每个点是否为陆地且未被访问,然后从该点开始进行DFS遍历。在DFS中,会检查周围的四个方向,并对每个方向进行递归调用,直到所有相邻的陆地都被访问。文章还提到了可以通过将访问过的陆地直接设置为水来避免使用额外的数组,但这种方法会改变原始网格。

文章还讨论了在面试中可能遇到的时间和空间复杂度问题,以及如何处理递归调用栈的空间复杂度。最后,文章列出了与该问题相似或延伸的问题,并对读者提出的建议和改进表示感谢。作者还邀请读者提供反馈和建议,并鼓励读者关注并点赞文章。

观点

  1. ** islands number problem**: 通过给定的二维网格,计算岛屿数量,这是一个典型的DFS问题。
  2. DFS algorithm: 解决该问题的关键在于使用DFS算法,通过递归遍历每个岛屿的每个点,并标记已访问的点以避免重复。
  3. Avoiding extra space: 提出了一种替代方法,即直接将访问过的陆地转换为水,以节省空间,但这会改变原始数据。
  4. Complexity analysis: 分析了算法的时间和空间复杂度,指出在最好情况下为O(m*n),如果考虑递归栈,空间复杂度为O(N)。
  5. Similar and extended problems: 指出了与该问题相关的其他问题,如“Surrounded Regions”和“Max Area of Island”,这些问题也是基于DFS算法的。
  6. Reader engagement: 鼓励读者参与讨论,提供反馈,并通过点赞和关注来支持作者。

從LeetCode學演算法 - 45 DFS (1)

0200 Number of Islands (Medium)

寫在前面: 如果覺得這篇有幫助到您的話, 請幫我隨手點開最下面的SHOW EMBED並拍5下手~ (當然,Medium的拍手也可以多拍幾下啦XD) 這樣我再寫個十多篇以後應該就有買一杯咖啡的$$了XDDDDD (聽起來超少哈哈)

Question: Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. 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 1:

Input:
11110
11010
11000
00000
Output: 1

Example 2:

Input:
11000
11000
00100
00011
Output: 3

分析/解題: 給定一個二維的地圖當中陸地用'1'表示,水則用'0'表示, 試計數這整張圖有多少島嶼(island)。可假定地圖的外圍均為水域。 (島嶼的定義就是其周圍都圍繞著水, 且可以通過連接相鄰的垂直/水平陸地所構成。)

如對於題目描述依舊沒搞懂的話,就把它當成小畫家填色的色塊吧! 視為相同區域的用油漆桶可以全部填滿XD 而題目所問的,就相當於要填幾次才能將所有陸地區域填滿

對於要確認島嶼這點,只有一種方法: 每次從一個點開始向外延伸相鄰的格子, 直到周圍沒有可以延伸的區域為止。 這時候這整塊區域就是我們所謂的島嶼,於是計數可以加1。 那麼問題來了,在延伸的過程中一共有四個方向可以選擇, 如果不加限制的話,無疑會有重複的部分; 所以除了檢查是否遇到水(‘0’,也就是可以不用繼續往下搜尋)外, 我們還需要知道這個格子是否已經走過了。 所以我們會再新增一個visited陣列用來標明已經走過的區域。 (這樣子只要已經走過的就直接跳過即可。)

由於每次會需要走到底才會回頭, 所以這種方式是很典型的DFS(Depth-First Search,深度優先搜尋)方法。

所以流程會變成: 1. 初始化一個visited二維陣列,預設當中每個值均為false 2. 對grid中的每個點(i, j)檢查其是否為陸地(‘1’),且未曾走訪過的話: 2-a. 遞增島嶼計數值,並從每個點(i, j)呼叫函式dfs來開始走訪 3. 在dfs中,先檢查(i, j)點是否符合規範(因會+1或-1,須不超出範圍), 且尚未走訪過(visited[i][j] == false)的狀況,進行3-a的流程 3-a. 將visited[i][j]設定為true 3-b. 往(i, j)的上下左右走,並傳入下一階段的dfs。

這麼一來,當整張圖掃過去以後, 我們就能知道到底有幾個島嶼了!

Java和Python的寫法基本一致,這邊不再贅述, 讀者只要注意到應該被檢查的邊界條件即可。

Java:

Python:

在這個範例中我們使用了一個額外的陣列來儲存, 有沒有辦法能夠不用額外的陣列呢? 其實是有的,辦法就是將每次走過的陸地(‘1’), 都直接設定成水(‘0’),自然就相當於都走過了, 從而可以替代掉用visited來處理。 優點是速度會較為快速, 但很嚴重的缺點是這麼做完以後, grid本身就會變成全部都是’0'的陣列了。 除非已經確定這個grid可以被任意操作, 否則筆者認為要這麼做的話記得要先問清楚要求才下手才行XD~

此外,這題在返回時仍舊要保留走過哪些格子的資訊, 所以和之前的Backtracking的題目作法是不一樣的。

面試實際可能會遇到的問題: 「時間/空間複雜度?」 (若m, n為長寬,O(m*n) for best case,理論上全部走訪一遍即可, O(m*n)) (如果可以清空grid則記錄用的空間複雜度是O(1)) (若考慮call stack的部份的話,總空間複雜度worst case是O(N))

相似及延伸: 1. 0130. Surrounded Regions 2. 0695. Max Area of Island

Special Thanks suggestions/corrections from viewers: Raiy Kuo: DFS時recusive占用的stack應一併考慮進去。

(歡迎提供意見協助更正歐~)

從LeetCode學演算法,我們下次見囉,掰~

如果你/妳覺得這篇文章不錯,請給我5個拍手,並且給我5Like。(點開SHOW EMBED!)
如果你/妳非常喜歡這篇文章,可以按著拍手直到50下。
也請記得「Follow」我,隨時收看最新的文章。
Leetcode
Python
Java
Programming
Algorithms
Recommended from ReadMedium