On this page
ข้อ 44 · LC547 Number of Provinces (นับจำนวนจังหวัด) 🟡
นับ connected components iterate node ทุกตัว เจอตัวที่ยังไม่ visited คือแคว้นใหม่ แล้ว DFS กวาดให้หมด
โจทย์ (LC547): มี n เมือง บางเมืองเชื่อมกันโดยตรง บางเมืองไม่เชื่อม กำหนด n x n matrix ชื่อ isConnected โดย isConnected[i][j] = 1 หมายถึงเมือง i กับเมือง j เชื่อมกันโดยตรง และ isConnected[i][j] = 0 หมายถึงไม่เชื่อม province คือกลุ่มของเมืองที่เชื่อมถึงกันได้ทั้งทางตรงและทางอ้อม ให้ return จำนวน province ทั้งหมด
- Input:
- isConnected = [[1,1,0],[1,1,0],[0,0,1]]
- Output:
- 2
- Explanation:
- เมือง 0 กับ 1 เชื่อมกันโดยตรงเป็นแคว้นเดียว เมือง 2 ไม่เชื่อมกับใครเลย อยู่คนเดียวอีกแคว้น
- Input:
- isConnected = [[1,0,0],[0,1,0],[0,0,1]]
- Output:
- 3
- Explanation:
- ไม่มีเมืองไหนเชื่อมกันเลย แต่ละเมืองจึงเป็นแคว้นของตัวเอง
- 1 <= n <= 200
- n == isConnected.length == isConnected[i].length
- isConnected[i][j] เป็น 0 หรือ 1
- isConnected[i][i] == 1 และ isConnected[i][j] == isConnected[j][i]
graph ให้มาในรูป adjacency matrix (ตาราง) ไม่ใช่ list เพื่อนบ้าน (neighbor) ของเมือง i คือช่องที่เป็น 1 ในแถว i
แนวทาง — ต้องใช้อะไร & คิดยังไง
นี่คือการนับ connected components คือกลุ่มก้อนของ node (โหนด) ที่เชื่อมถึงกันได้ใน undirected graph (กราฟไม่มีทิศ) ใช้ DFS traverse (เดินไล่) กวาดแต่ละกลุ่ม เลือก DFS เพราะเราแค่ต้องกวาดให้ทั่วทั้งกลุ่มเพื่อ mark ว่าเมืองพวกนี้อยู่แคว้นเดียวกันแล้ว
เทคนิคนับกลุ่มคือ iterate (วน) node ทุกตัวจากข้างนอก ถ้าเจอตัวที่ยังไม่ visited (เคยเยือน) แสดงว่าเราเพิ่งสะดุดเข้าแคว้นใหม่ที่ยังไม่เคยแตะ บวกตัวนับหนึ่ง แล้วยิง DFS เข้าไปกวาดทุกเมืองในแคว้นนั้นให้ visited จนหมด พอ loop (วน) นอกเดินต่อ เมืองที่อยู่แคว้นเดิมจะถูก visited แล้วจึงไม่ถูก count (นับ) ซ้ำ
- อ่านจำนวนเมือง n จากขนาดตาราง แล้วสร้าง set visited
- เขียน dfs(city) ที่ mark city แล้ว iterate ดูทุกเมือง other ในแถวนั้น
- ถ้า isConnected[city][other] เป็น 1 และ other ยังไม่ visited ให้ dfs(other) ต่อ
- ตั้งตัวนับ provinces = 0 แล้ว iterate city ทุกเมืองจากข้างนอก
- ถ้า city ยังไม่ visited บวก provinces หนึ่ง แล้ว dfs(city) กวาดทั้งแคว้น
- คืน provinces
count province ทุกครั้งที่เข้า dfs ซึ่งจะเกิน ต้อง count เฉพาะตอนเริ่มแคว้นใหม่ใน loop นอกเท่านั้น (ตอนเจอเมืองที่ยังไม่ visited)
ไล่ทีละสเต็ป
รันบน isConnected = [[1,1,0],[1,1,0],[0,0,1]] (loop นอก iterate city 0,1,2)
| city | visited แล้ว? | ทำอะไร | provinces |
|---|---|---|---|
| 0 | ยัง | แคว้นใหม่ +1, dfs(0) กวาดถึง 1 ด้วย | 1 |
| 1 | แล้ว (จาก dfs 0) | ข้าม | 1 |
| 2 | ยัง | แคว้นใหม่ +1, dfs(2) อยู่คนเดียว | 2 |
▶ เฉลยละเอียด (ลองเองก่อนนะ)
def find_circle_num(is_connected):
n = len(is_connected)
visited = set()
def dfs(city):
visited.add(city)
for other in range(n):
# เชื่อมกันโดยตรง และยังไม่เคยไป
if is_connected[city][other] == 1 and other not in visited:
dfs(other)
provinces = 0
for city in range(n):
if city not in visited: # เจอเมืองที่ยังไม่อยู่แคว้นไหน
provinces += 1 # นับเป็นแคว้นใหม่
dfs(city) # กวาดทุกเมืองในแคว้นนี้
return provinces
print(find_circle_num([[1, 1, 0], [1, 1, 0], [0, 0, 1]])) # 2
print(find_circle_num([[1, 0, 0], [0, 1, 0], [0, 0, 1]])) # 32
3connected components คือ กลุ่มก้อน ของ node ที่เชื่อมถึงกันได้ใน undirected graph โจทย์นี้ให้ graph มาในรูปตาราง (adjacency matrix) แทน list โดย is_connected[i][j] บอกว่าเมือง i กับ j เชื่อมกันไหม เราจึงหาเพื่อนบ้านของเมืองด้วยการไล่ทั้งแถวดูว่าช่องไหนเป็น 1
เทคนิคนับกลุ่มคือ iterate node ทุกตัวจากนอก ถ้าเจอตัวที่ยังไม่ visited แสดงว่าเราเพิ่งสะดุดเข้าแคว้นใหม่ที่ยังไม่เคยแตะ บวกตัวนับหนึ่ง แล้วยิง DFS เข้าไปกวาดทุกเมืองในแคว้นนั้นให้ visited จนหมด พอ loop นอกเดินต่อ เมืองที่อยู่แคว้นเดิมจะถูก visited แล้วจึงไม่ถูก count ซ้ำ
Time O(n^2) เพราะต้อง iterate ตาราง n x n · Space O(n) จาก visited และ call stack
นับกลุ่ม / เกาะ / แคว้น = นับ connected components iterate node ทุกตัว เจอตัวที่ยังไม่ visited คือกลุ่มใหม่ +1 แล้ว DFS กวาดทั้งกลุ่ม ใช้ได้กับโจทย์ number of islands และเพื่อน ๆ ทั้งหมด