方法:二部图染色
根据输入的数组建图,其中dislike的pair,表示图中的一个edge。
问题转化为,可否把图的节点分为两个集合,其中集合内部的节点在图中不相邻。
- To solve this problem, we can start a BFS traversal over the graph of people and start assigning the colors.
- We can assign
REDto the starting node. The colorBLUEshould then be assigned to all of the neighbors. Neighbors ofBLUEnodes should have the colorRED, and so on (keep alternating colors between neighbors). - Let's use
0forREDand1forBLUE. To flip the colors, we can use the formula1 - color - 如果发现邻居颜色相同,则有冲突,说明无法被分成两个集合。
- There can be multiple connected components in the graph(多个联通图). We need to visit all of them to make sure we visit every node.
class Solution {
public boolean possibleBipartition(int n, int[][] dislikes) {
// 构建邻居列表
Map<Integer, List<Integer>> adj = new HashMap<>();
for (int[] arr : dislikes) {
if (!adj.containsKey(arr[0])) {
adj.put(arr[0], new ArrayList<>());
}
adj.get(arr[0]).add(arr[1]);
if (!adj.containsKey(arr[1])) {
adj.put(arr[1], new ArrayList<>());
}
adj.get(arr[1]).add(arr[0]);
}
// 每个节点颜色,一开始全填充-1,表示未上色
int[] color = new int[n + 1];
Arrays.fill(color, -1);
for (int i = 1; i <= n; i++) {
// 找到一个独立的图,因为可能有多个联通图
if (color[i] == -1) {
// 从当前节点开始bfs染色
if (!bfs(i, adj, color)) {
return false; // 当前的图染色有冲突
}
}
}
return true;
}
// bfs, 从source节点开始,给邻居染色,有冲突返回false
public boolean bfs(int source, Map<Integer, List<Integer>> adj, int[] color) {
Queue<Integer> queue = new LinkedList<>();
queue.offer(source);
color[source] = 0; // 当前节点染色
while (!queue.isEmpty()) {
int cur = queue.poll();
if (!adj.containsKey(cur)) { // 没有adj列表的node
continue;
}
// 遍历邻居,给他们上色
for (int nbr : adj.get(cur)) {
if (color[nbr] == color[cur]) {
return false; // 邻居颜色和当前一样,冲突
} else if (color[nbr] == -1) {
color[nbr] = 1 - color[cur]; // 另一种颜色
queue.add(nbr);
}
}
}
return true;
}
}