题目链接
题目描述
给定一个包含 个节点和
条边的无向图。如果能将图中的节点染成黑白两种颜色,使得每条边的两个端点颜色都不同,那么这个图就被称为二分图。
任务是判断给定的图是否为二分图。
解题思路
判断一个图是否为二分图的经典方法是染色法。一个图是二分图,当且仅当它不包含任何奇数长度的环。我们可以通过图遍历(如广度优先搜索 BFS 或深度优先搜索 DFS)来模拟这个染色过程。
算法的核心思想是:尝试用两种颜色(例如,颜色 1 和颜色 2)对图中的节点进行染色。从任意一个未被染色的节点开始遍历,将其染成颜色 1。然后,将其所有相邻的节点染成颜色 2。再将与颜色 2 的节点相邻的未染色节点染成颜色 1,以此类推。
在遍历过程中,如果遇到一个已经被染色的相邻节点,我们就检查它的颜色。如果这个相邻节点的颜色与当前节点的颜色相同,说明这两个直接相连的节点颜色冲突了。这也就意味着图中存在一个奇数长度的环,因此该图不是二分图。
由于给定的图可能是不连通的(由多个独立的连通分量组成),我们需要对每个连通分量都执行一次染色检查。
算法步骤如下:
-
初始化:
- 构建图的邻接表。
- 创建一个
$color$
数组,大小为,用于存储每个节点的颜色。初始时所有值设为 0,表示“未染色”。
-
遍历所有节点:
- 从节点 1 到
进行循环。
- 如果当前节点
尚未被染色(
),说明我们发现了一个新的连通分量。从这个节点开始执行一次染色过程(例如,使用 BFS)。
- 从节点 1 到
-
BFS 染色过程:
- 创建一个队列,并将起始节点
加入队列。将其染上第一种颜色(
)。
- 当队列不为空时,出队一个节点
。
- 遍历
的所有邻居节点
:
- 如果
未被染色(
),则将其染成与
相反的颜色(
),并将其入队。
- 如果
已经被染色,检查
是否与
相同。如果相同,则发生冲突,说明图不是二分图,可以直接返回 "No"。
- 如果
- 创建一个队列,并将起始节点
-
最终结果:
- 如果遍历完所有节点及其连通分量都没有发现任何颜色冲突,则说明该图是二分图,返回 "Yes"。
代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
bool isBipartite(int n, const vector<vector<int>>& adj) {
vector<int> color(n + 1, 0);
for (int i = 1; i <= n; ++i) {
if (color[i] == 0) {
queue<int> q;
q.push(i);
color[i] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (color[v] == 0) {
color[v] = 3 - color[u];
q.push(v);
} else if (color[v] == color[u]) {
return false;
}
}
}
}
}
return true;
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n + 1);
for (int i = 0; i < m; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
if (isBipartite(n, adj)) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
ArrayList<Integer>[] adj = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) {
adj[i] = new ArrayList<>();
}
for (int i = 0; i < m; i++) {
int u = sc.nextInt();
int v = sc.nextInt();
adj[u].add(v);
adj[v].add(u);
}
if (isBipartite(n, adj)) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
private static boolean isBipartite(int n, ArrayList<Integer>[] adj) {
int[] color = new int[n + 1];
for (int i = 1; i <= n; i++) {
if (color[i] == 0) {
Queue<Integer> q = new LinkedList<>();
q.add(i);
color[i] = 1;
while (!q.isEmpty()) {
int u = q.poll();
for (int v : adj[u]) {
if (color[v] == 0) {
color[v] = 3 - color[u];
q.add(v);
} else if (color[v] == color[u]) {
return false;
}
}
}
}
}
return true;
}
}
from collections import deque
def is_bipartite(n, adj):
color = [0] * (n + 1)
for i in range(1, n + 1):
if color[i] == 0:
q = deque([i])
color[i] = 1
while q:
u = q.popleft()
for v in adj[u]:
if color[v] == 0:
color[v] = 3 - color[u]
q.append(v)
elif color[v] == color[u]:
return False
return True
n, m = map(int, input().split())
adj = [[] for _ in range(n + 1)]
for _ in range(m):
u, v = map(int, input().split())
adj[u].append(v)
adj[v].append(u)
if is_bipartite(n, adj):
print("Yes")
else:
print("No")
算法及复杂度
- 算法:图的二分图判定(染色法),通常通过广度优先搜索(BFS)或深度优先搜索(DFS)实现。
- 时间复杂度:
,其中
是节点数,
是边数。因为每个节点和每条边在遍历过程中最多被访问一次。
- 空间复杂度:
,主要用于存储图的邻接表、颜色数组以及 BFS 使用的队列。