1. 问题描述:
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环。请你判断这个图是否是二分图。
输入格式
第一行包含两个整数 n 和 m。接下来 m 行,每行包含两个整数 u 和 v,表示点 u 和点 v 之间存在一条边。
输出格式
如果给定图是二分图,则输出 Yes,否则输出 No。
数据范围
1 ≤ n,m ≤ 10 ^ 5
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
来源:https://www.acwing.com/problem/content/description/862/
2. 思路分析:
二分图,又称为二部图,节点由两个集合组成,且两个集合内部没有边的图,下面是一个二分图的直观表示,可以发现边存在于两个集合之间,集合内部是没有边的:
性质:如果两个集合中的点分别染成黑色和白色,可以发现二分图中的每一条边都一定是连接一个黑色点和一个白色点。二分图不存在长度为奇数的环。一个图是二分图等价于使用染色法染色不存在矛盾,所以我们可以在遍历节点的过程中对节点进行染色,0表示未染色,1和2表示两种相反的颜色,当节点未染色的时候对其染色即可,如果发现某个点已经染色了并且其邻接点是相同的颜色说明存在矛盾,也即不是二分图直接返回False,如果染色的过程中不存在矛盾说明是二分图。遍历节点可以使用dfs或者bfs,因为数据规模比较大所以推荐使用bfs,python语言使用dfs来解决就超时了。
3. 代码如下:
bfs:
import collections
from typing import List
class Solution:
# bfs遍历以当前u为根节点的连通块
def bfs(self, u: int, st: List[int], g: List[List[int]]):
q = collections.deque([u])
st[u] = 1
while q:
p = q.popleft()
for next in g[p]:
if st[next] == 0:
q.append(next)
st[next] = 3 - st[p]
# 遍历邻接点的颜色是否相同, 相同则说明同一连通块的颜色是一样的
elif st[next] == st[p]:
return False
return True
def process(self):
n, m = map(int, input().split())
g = [list() for i in range(n + 10)]
for i in range(m):
a, b = map(int, input().split())
# 无向图
g[a].append(b)
g[b].append(a)
st = [0] * (n + 10)
flag = 1
# 存在多个连通块所以要循环遍历
for i in range(1, n + 1):
if st[i] == 0:
if not self.bfs(i, st, g):
flag = 0
break
if flag: return "Yes"
return "No"
if __name__ == "__main__":
print(Solution().process())
dfs:果然使用dfs来写超时了,python使用dfs很大一个缺点是非常容易超时:
from typing import List
import sys
class Solution:
# 遍历节点的时候进行染色
def dfs(self, u: int, color: int, st: List[int], g: List[List[int]]):
st[u] = color
for next in g[u]:
if st[next] == 0:
# 0表示未染色, 1和2表示相反的颜色
self.dfs(next, 3 - color, st, g)
# 邻接点的颜色相同返回false
elif st[next] == color:
return False
return True
def process(self):
n, m = map(int, input().split())
g = [list() for i in range(n + 10)]
for i in range(m):
a, b = map(int, input().split())
# 无向图
g[a].append(b)
g[b].append(a)
st = [0] * (n + 10)
flag = 1
for i in range(1, n + 1):
if st[i] == 0:
# 当前连通块存在矛盾
if not self.dfs(i, 1, st, g):
flag = 0
break
if flag: return "Yes"
return "No"
if __name__ == "__main__":
# 设置递归的最大调用次数
sys.setrecursionlimit(50000)
print(Solution().process())