785. 判斷二分圖
給定一個無向圖graph,當這個圖為二分圖時傳回true。
如果我們能将一個圖的節點集合分割成兩個獨立的子集A和B,并使圖中的每一條邊的兩個節點一個來自A集合,一個來自B集合,我們就将這個圖稱為二分圖。
graph将會以鄰接表方式給出,graph[i]表示圖中與節點i相連的所有節點。每個節點都是一個在0到graph.length-1之間的整數。這圖中沒有自環和平行邊: graph[i] 中不存在i,并且graph[i]中沒有重複的值。
示例 1:
輸入: [[1,3], [0,2], [1,3], [0,2]]
輸出: true
解釋:
無向圖如下:
0----1
| |
| |
3----2
我們可以将節點分成兩組: {0, 2} 和 {1, 3}。
示例 2:
輸入: [[1,2,3], [0,2], [0,1,3], [0,2]]
輸出: false
解釋:
無向圖如下:
0----1
| \ |
| \ |
3----2
我們不能将節點分割成兩個獨立的子集。
注意:
graph 的長度範圍為 [1, 100]。
graph[i] 中的元素的範圍為 [0, graph.length - 1]。
graph[i] 不會包含 i 或者有重複的值。
圖是無向的: 如果j 在 graph[i]裡邊, 那麼 i 也會在 graph[j]裡邊。
深度優先搜尋
每一次都染色一個之後與其相接的點進行染色判斷,先找到一個沒被染色的節點u,把它染上一種顔色,之後周遊所有與它相連的節點v,如果節點v已被染色并且顔色和節點u一樣,那麼就不是二分圖。如果這個節點v沒有被染色,先把它染成與節點u不同顔色的顔色,然後周遊所有與節點v相連的節點…如此遞歸下去。
from typing import List
##深度優先搜尋,每一次都染色一個之後鬥魚其相接的點進行染色判斷
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
n = len(graph)#計算有多少個點
UNCOLORED, RED, BLUE = 0, 1, 2#設定三種顔色
record = [UNCOLORED] * (n)#為這些點建立一個清單,記錄他們的顔色
valid = True#初始化預設是可分的
#用于檢驗目前染色是否符合要求
def dfs(i, color):
nonlocal valid
record[i] = color#将目前點進行染色
dif_color = BLUE if color == RED else RED#取一個相反的顔色
for j in range(len(graph[i])):#對本次染色點的鄰接點進行染色判斷
if record[graph[i][j]] == UNCOLORED:#緊鄰點如果無色,進行染相反色,然後再次遞歸
dfs(graph[i][j],dif_color)
if valid == False:##這裡很重要,遞歸之後對valid進行判斷,如果為False,放棄遞歸,直接傳回
return#是以每當遞歸的過程中出現了一個False之後,就會從這裡,一步一步往上跳出程式
elif record[graph[i][j]] == dif_color:#鄰接點已經染了相反顔色,繼續下一個鄰接點判斷
continue
else:#如果鄰接點染的相同顔色,valid 指派 False,傳回本次遞歸
valid = False
return
for i in range(n):#對n個點進行循環判斷
if record[i] == UNCOLORED:#如果沒有被染色,那麼将其然染色後進行判斷
dfs(i, RED)#深度優先判斷,如果不可分,則會将valid指派為False
if valid == False:#隻要傳回一個False,那麼程式結束
break
return valid
########### DFS簡潔寫法
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
vis = [0] * len(graph)
def dfs(pos, color):
vis[pos] = color
for i in graph[pos]:
# 顔色相同 or (未通路 且 繼續深搜為False)
# 可直接傳回False
if vis[i] == color or not (vis[i] or dfs(i, -color)):
return False
return True
# 不一定為聯通圖,需周遊每一個節點
for i in range(len(graph)):
if not vis[i] and not dfs(i, 1):
return False
return True
# 作者:GiaoGiaoWo
# 連結:https://leetcode-cn.com/problems/is-graph-bipartite/solution/python3-dfs-ji-jian-dai-ma-shi-jian-88kong-jian-10/
# 來源:力扣(LeetCode)
# 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
###########
BFS 的核心就是隊列
首先進入for 循環,加一個節點進來,染色。尋找與其相接的點是否染色,如果染色一樣,
傳回False。顔色不一樣,繼續看其他相鄰點。遇到沒有染色的節點,此時染不同色,同時加入清單中,因為染色後就要找它相鄰節點。
1.for 循環, 看目前節點是否已經染色,如果染色就看下一節點,否則就預設染成紅色(大多數的節點在while queue中就已經被染色)
2.for循環,對目前已經才染色過,并且加入隊列的點,清單循環,每次從清單彈出一個節點,看和其相鄰的節點是否染色,
如果已經染色并且顔色相同,直接傳回False;
如果染色,但是顔色不同,什麼也不做,看其他相鄰點
如果還未染色,這次染成不一樣的顔色,加入隊列,看是否可分
3.循環結束都沒有False,那麼說明可分,傳回True
注意:
這裡沒有用雙向隊列,因為可以不考慮順序,每個節點的相鄰節點都會被判斷,不用考慮順序
#********** 本題還可以使用BFS來做 *************#
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
n = len(graph)
record = [0] * n
UNCOLORED, RED, BLUE = 0, 1, -1#設定三種顔色
for i in range(n):
if record[i] != UNCOLORED:
continue
record[i] = RED#如果沒有填色,則預設填紅色
queue = [i]#将染色後的序号加入清單
while queue:
node = queue.pop()#彈出目前已染色的節點索引
for block in graph[node]:#和已染色的點相鄰的快
if record[block] == UNCOLORED:#m沒有染色,染成不同的顔色
record[block] = BLUE if record[node] == RED else RED
queue.append(block)
elif record[block] == record[node]:#如果顔色相同,傳回False
return False
return True
#******************************************************#
if __name__ == "__main__":
s = Solution()
graph = [[1,3], [0,2], [1,3], [0,2]]
graph = [[1,2,3], [0,2], [0,1,3], [0,2]]
print(s.isBipartite(graph))