一魚多吃: 從 島嶼周長 理解 圖論演算法的本質

小松鼠-avatar-img
發佈於演算法題目解析 個房間
更新於 發佈於 閱讀時間約 11 分鐘

今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。
題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。


英文的題目敘述在這裡


題目敘述

題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋

要求我們以四連通N4的方式拜訪這座島嶼,並且計算這座島嶼的周長

題目保證整張地圖只會有一座島嶼


測試範例

Example 1:

raw-image
Input: grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
Output: 16
黃色部分的長度代表這座島嶼的周長​
Explanation: The perimeter is the 16 yellow stripes in the image above.

Example 2:

Input: grid = [[1]]
Output: 4

Example 3:

Input: grid = [[1,0]]
Output: 4

約束條件

Constraints:

  • row == grid.length

row的總數 = 地圖的高度

  • col == grid[i].length

col的總數 = 地圖的寬度

  • 1 <= row, col <= 100

高度和寬度都介於 1~100之間。

  • grid[i][j] is 0 or 1.

每個格子點一定是零(代表海洋)或一(代表陸地)。

  • There is exactly one island in grid.

保證整張地圖只會有一座島嶼。


什麼時候會遇到島嶼的邊界?

當遇到陸地和海洋交界的時候。

當遇到地圖邊界的時候。

這時候剛好跨過島嶼的邊界,周長累加一


用DFS 探索相連的陸地


先找出的第一塊陸地的格子點,接著用N4 四連通 配合深度優先DFS探索
周圍相鄰的陸地格子點。

當我們遇到陸地和海洋的交界,或者我們遇到地圖邊界時,
這時候剛好跨過島嶼的邊界,周長累加一


時間複雜度O(H * W) 每個格子點最多拜訪一次。

空間複雜度O(H * W) 遞回深度最深是幾乎拜訪整張圖。


DFS 程式碼

class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:

H, W = len(grid), len(grid[0])

def dfs(r, c):

if not ( 0 <= r < H ) or not( 0 <= c < W) or grid[r][c] == 0:
return 1

if grid[r][c] == '#':
return 0

N4_offset = itertools.pairwise([0, 1, 0, -1, 0])

perimeter = 0
for dr, dc in N4_offset:
nr, nc = r+dr, c+dc

grid[r][c] = '#'
perimeter += dfs(nr, nc)

return perimeter

# ==================================

for r in range(H):
for c in range(W):
if grid[r][c] == 1:
return dfs(r, c)

return -1

用BFS 探索相連的陸地


先找出的第一塊陸地的格子點,接著用N4 四連通 配合廣度優先BFS探索
周圍相鄰的陸地格子點。

當我們遇到陸地和海洋的交界,或者我們遇到地圖邊界時,
這時候剛好跨過島嶼的邊界,周長累加一


時間複雜度O(H * W) 每個格子點最多拜訪一次。

空間複雜度O(H * W) Queue長度最長是幾乎拜訪整張圖。


BFS 程式碼

class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:

H, W =len(grid), len(grid[0])

perimeter = 0

N4 = list( itertools.pairwise([0, 1, 0, -1, 0]) )

for r in range(H):
for c in range(W):
if grid[r][c] == 1:

bfs_queue = deque([(r,c)])

while bfs_queue:

cur_r, cur_c = bfs_queue.popleft()

#print(f"at {cur_r}, {cur_c}")

if not( 0 <= cur_r < H ) or not( 0 <= cur_c < W ) or grid[cur_r][cur_c] == 0:
# update perimeter count
perimeter += 1
continue

if grid[cur_r][cur_c] == '#':
# Avoid repeated traversal
continue

# Mark current grid as visited
grid[cur_r][cur_c] = '#'

for dr, dc in N4:
nr, nc = cur_r + dr, cur_c + dc
bfs_queue.append( (nr, nc) )

return perimeter

return -1

依序掃描格子點的演算法


除了經典的DFS或者BFS探索演算法之外,我們也可以用掃描格子點的方式。

依序從左到右,從上到下掃描每個格子點。

統計遇到的陸地格子點數目,再統計內部邊緣(就是陸地和陸地相鄰的邊緣)有幾條。

每個陸地格子點會貢獻四條邊緣
內部邊緣會被兩塊相鄰的陸地所共用,所以重複計算了兩次


最後,島嶼的周長

= 陸地格子點貢獻的邊緣數目 - 重複計算的內部邊緣

= 陸地格子點 * 4 - 內部邊緣總數 * 2


時間複雜度O(H * W) 每個格子點最多拜訪一次。

空間複雜度O(1) 只有使用固定尺寸的臨時變數,所需空間為常數級別。


依序掃描格子點的程式碼

class Solution:
def islandPerimeter(self, grid: List[List[int]]) -> int:

h, w = len(grid), len(grid[0])

if h * w == 0:
# Quick response for simple case
return 0

land_cell, internal_edge = 0, 0

# scan each cell from top to bottom, from left to right
for y in range(h):
for x in range(w):

if grid[y][x] == 1:
# current cell is land
land_cell += 1

if y and grid[y-1][x]:
# current land cell share one internal edge with neighbor land cell on the top
internal_edge += 1

if x and grid[y][x-1]:
# current land cell share one internal edge with neighbor land cell of the left
internal_edge += 1


# each land cell contributes 4 edge
# each internal edge is repeatedly counted by 2 adjacent land cells
perimeter = land_cell * 4 - internal_edge * 2

return perimeter

關鍵知識點 找出共通模式


根據題意,找出島嶼邊緣的定義。


可以根據範例推敲發現:

當遇到陸地和海洋交界的時候。
當遇到地圖邊界的時候。
這時候剛好跨過島嶼的邊界,周長累加一

結語

條條大路通羅馬,萬變不離其宗。

一個題目可以有好多種解法,但是背後核心觀念都是類似的

只要掌握基本原理,根據題意,找出目標的明確定義,
接著採用合適的圖論演算法去計算所求(島嶼的周長)即可!


希望能幫助讀者、觀眾徹底理解基本圖論演算法BFS / DFS 的原理,

並且能夠舉一反三,洞察其背後的本質和了解相關的應用領域!


感謝收看囉! 我們下篇文章再見~

留言
avatar-img
留言分享你的想法!
avatar-img
小松鼠的演算法樂園
96會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/09/26
Leetcode 729. My Calendar I 給定一個行事曆的class定義和行程安排的介面interface。 請完成下列function 1.建構子MyCalendar() 初始化MyCalendar物件 2.boolean book(int start, int end) 插入新行程
Thumbnail
2024/09/26
Leetcode 729. My Calendar I 給定一個行事曆的class定義和行程安排的介面interface。 請完成下列function 1.建構子MyCalendar() 初始化MyCalendar物件 2.boolean book(int start, int end) 插入新行程
Thumbnail
2024/09/10
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
2024/09/10
Insert Greatest Common Divisors in Linked List 題目給定一個鏈結串列, 請在兩兩節點之間加入一個新節點,新節點的值為兩者之間的最大公因數。 最後返回新串列的head node作為答案。
Thumbnail
2024/09/09
2326. Spiral Matrix IV 題目給定一個Linked list和對應的矩陣高度m、寬度n。 請依照順時針的拜訪順序, 從左上角出發,依照次序把Linked List的內容填到矩陣裡。 如果有剩餘不足的空位,就填補-1。 最後將填補好的矩陣返回作為答案。
Thumbnail
2024/09/09
2326. Spiral Matrix IV 題目給定一個Linked list和對應的矩陣高度m、寬度n。 請依照順時針的拜訪順序, 從左上角出發,依照次序把Linked List的內容填到矩陣裡。 如果有剩餘不足的空位,就填補-1。 最後將填補好的矩陣返回作為答案。
Thumbnail
看更多
你可能也想看
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
孩子寫功課時瞇眼?小心近視!這款喜光全光譜TIONE⁺光健康智慧檯燈,獲眼科院長推薦,網路好評不斷!全光譜LED、180cm大照明範圍、5段亮度及色溫調整、350度萬向旋轉,讓孩子學習更舒適、保護眼睛!
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
子集合生成是一道經典的組合類上機考和面試題目。本篇文章介紹多個不同的解決方案,以及相關演算法框架。主要目標是給定n個相異的元素,產生所有的子集合。
Thumbnail
子集合生成是一道經典的組合類上機考和面試題目。本篇文章介紹多個不同的解決方案,以及相關演算法框架。主要目標是給定n個相異的元素,產生所有的子集合。
Thumbnail
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
Thumbnail
今天的一魚三吃系列是透過 兩點之間是否存在一條路徑的題目,來回顧以前學過的DFS、BFS和Disjoint Set,鞏固圖論演算法的知識點。 英文的題目敘述在這裡 題目敘述 給定我們已知n個節點的圖,和圖上的每一條無向邊edges。 請問給定的起點start和終點end是否存在一條路徑可
Thumbnail
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
Thumbnail
今天的官方每日一題是Island Perimeter島嶼周長,很有趣的一題。 題目非常直觀好懂。也很適合拿來作為多角度複習、回顧圖論演算法的好題目。 英文的題目敘述在這裡 題目敘述 題目會給我們一個二維陣列當作地圖,格子點為1代表陸地,格子點為0代表海洋。 要求我們以四連通N4的方式拜訪
Thumbnail
題目敘述 給定兩個字串word1和word2,每次操作時,可以有三個選項 插入一個字元 刪除一個字元 替換一個字元 請問把word1轉換成word2的最小操作次數是多少? 題目的原文敘述 約束條件 Constraints: 0 <= word1.length, word2.le
Thumbnail
題目敘述 給定兩個字串word1和word2,每次操作時,可以有三個選項 插入一個字元 刪除一個字元 替換一個字元 請問把word1轉換成word2的最小操作次數是多少? 題目的原文敘述 約束條件 Constraints: 0 <= word1.length, word2.le
Thumbnail
題目敘述 題目會給定我們一個n值,要求我們列出從0 ~ n 之間,每個整數有幾個bit1,以陣列的形式返回答案。 例如n=3時 因為 0 = 0b 0 1 = 0b 1 2 = 0b 10 3 = 0b 11 輸出答案為[0, 1, 1, 2] 題目的原文敘述 測試範例 E
Thumbnail
題目敘述 題目會給定我們一個n值,要求我們列出從0 ~ n 之間,每個整數有幾個bit1,以陣列的形式返回答案。 例如n=3時 因為 0 = 0b 0 1 = 0b 1 2 = 0b 10 3 = 0b 11 輸出答案為[0, 1, 1, 2] 題目的原文敘述 測試範例 E
Thumbnail
題目敘述 題目會給定我們輸入邊長陣列nums,請問我們從裡面能夠建構出來的多邊形的最大周長是多少? 如果無解,返回-1 題目的原文敘述 約束條件 Constraints: 3 <= n <= 10^5 輸入陣列長度nums介於3 ~ 十萬 之間。 1 <= nums[i] <=
Thumbnail
題目敘述 題目會給定我們輸入邊長陣列nums,請問我們從裡面能夠建構出來的多邊形的最大周長是多少? 如果無解,返回-1 題目的原文敘述 約束條件 Constraints: 3 <= n <= 10^5 輸入陣列長度nums介於3 ~ 十萬 之間。 1 <= nums[i] <=
Thumbnail
題目敘述 題目會給定我們一個字串s,和一組字庫wordDict。 問我們能不能透過字串串接的方式,從字庫裡面的字拼成原本的字串s? 可以的話,返回True。 無解的話,返回False。 註: 題目還允許重複使用字庫裡面的字去串接。
Thumbnail
題目敘述 題目會給定我們一個字串s,和一組字庫wordDict。 問我們能不能透過字串串接的方式,從字庫裡面的字拼成原本的字串s? 可以的話,返回True。 無解的話,返回False。 註: 題目還允許重複使用字庫裡面的字去串接。
Thumbnail
題目敘述 題目給定我們一顆二元樹的根節點,要求我們計算出從根節點到葉子節點的偽回文路徑路徑有幾條? 偽回文路徑路徑 的定義: 路徑經過重新排列之後,可以形成回文Palindrome,也就是頭尾鏡像對稱。 ​ 例如: 1 -> 3 -> 3 重新排列之後,可以形成 3 -> 1 -> 3
Thumbnail
題目敘述 題目給定我們一顆二元樹的根節點,要求我們計算出從根節點到葉子節點的偽回文路徑路徑有幾條? 偽回文路徑路徑 的定義: 路徑經過重新排列之後,可以形成回文Palindrome,也就是頭尾鏡像對稱。 ​ 例如: 1 -> 3 -> 3 重新排列之後,可以形成 3 -> 1 -> 3
Thumbnail
題目敘述 題目會給我們一個二維陣列matrix,分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有三種選擇: 1.往左下角移動。 2.往正下方移動。 3.往右下角移動。 題目的原文敘述 測試範例 Example 1:
Thumbnail
題目敘述 題目會給我們一個二維陣列matrix,分別代表每個格子的成本,請問我們從最頂端到底部的下墜路徑的最小成本總和是多少? 每次下墜到下一排的時候,可以有三種選擇: 1.往左下角移動。 2.往正下方移動。 3.往右下角移動。 題目的原文敘述 測試範例 Example 1:
Thumbnail
題目會給定一個陣列,裡面有大有小,可以把數字的大小類比成高度 要求我們找出陣列裡面的相對極大值(relative max value)所在的陣列索引 也就是說當下這個元素,大於左邊鄰居的元素值,也大於右邊鄰居的元素值
Thumbnail
題目會給定一個陣列,裡面有大有小,可以把數字的大小類比成高度 要求我們找出陣列裡面的相對極大值(relative max value)所在的陣列索引 也就是說當下這個元素,大於左邊鄰居的元素值,也大於右邊鄰居的元素值
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News