Description
There is an undirected graph with n nodes, where each node is numbered between 0 and n - 1. You are given a 2D array graph, where graph[u] is an array of nodes that node u is adjacent to. More formally, for each v in graph[u], there is an undirected edge between node u and node v. The graph has the following properties:
There are no self-edges (graph[u] does not contain u).
There are no parallel edges (graph[u] does not contain duplicate values).
If v is in graph[u], then u is in graph[v] (the graph is undirected).
The graph may not be connected, meaning there may be two nodes u and v such that there is no path between them.
A graph is bipartite if the nodes can be partitioned into two independent sets A and B such that every edge in the graph connects a node in set A and a node in set B.
Return true if and only if it is bipartite.
Examples
Example 1:
Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
Output: false
Explanation: There is no way to partition the nodes into two independent sets such that every edge connects a node in one and a node in the other.
Example 2:
Input: graph = [[1,3],[0,2],[1,3],[0,2]]
Output: true
Explanation: We can partition the nodes into two sets: {0, 2} and {1, 3}.
Constraints:
graph.length == n
1 <= n <= 100
0 <= graph[u].length < n
0 <= graph[u][i] <= n - 1
graph[u] does not contain u.
All the values of graph[u] are unique.
If graph[u] contains v, then graph[v] contains u.
思路
我的思路还是分两步走,毕竟对图还不是很熟悉,所以最开始的时候想的就是用递归的方式暴力求解,这个方法需要建立两个数组 setA 和 setB,这两个数组就存储了二部图中的“二部”,即节点位置,那么每次递归的时候需要进行的判断和操作就是
- 起点和终点是不是同时包含在 A 或 B 中?是 → 不构成二部图
- 起点包含在 A 中 或 终点包含在 B 中?是 → 构建 A[起点]-B[终点] ,递归找下一条edge,并返回递归结果
- 起点包含在 B 中 或 终点包含在 A 中?是 → 构建 B[起点]-A[终点],递归找下一条edge,并返回递归结果
- 起点和终点 都不包含在 A/B 中?是 → 逐步讨论:构建 A[起点]-B[终点] ,递归找下一条edge
- 如果返回递归结果为 true,返回 true
- 如果返回递归结果为 false,恢复到构建 A[起点]-B[终点] 这一步之前的 A/B(因为在后续递归中会改变其他数值,所以不能单纯的只改动起点和终点对应的数值),然后构建 B[起点]-A[终点],归找下一条edge,并返回递归结果
当然上面这个方法不仅复杂,而且代码写起来也很繁琐。
后来就去偷了一下discussion,果然,可以用着色方法进行操作,同时,这种取决于one-move/two-moves 的着色方式会更适用于BFS(我这里用的是BFS,但是看accept里面DFS的速度会更快,不过我认为BFS写起来更容易理解,所以还是只用了BFS)
着色方法指的是:因为是构建二部图,所以如果我能将整张图的节点用两种颜色进行表示,且一条edge链接的两条节点都不属于同一个颜色,那他就是一张二部图。
对BFS来说,我设置当前节点为color1,那我的所有children(one-step-move)就应该都是color2,否则则不构成二部图
这里我用的是color = 1/-1,这样初始化的时候是0,可以区分visited和unvisited,同时通过-color直接获得下一层应该取得颜色
代码
递归解法
class Solution {
int[] setA, setB;
public boolean recurrsive(int[][] graph, int i, int j) {
while (j >= graph[i].length) {
i = i + 1;
j = 0;
if (i >= graph.length) {
return true;
}
}
int u = i;
int v = graph[i][j];
if ((setA[u] == 1 && setA[v] == 1) || (setB[u] == 1 && setB[v] == 1))
return false;
if (setA[u] == 1 || setB[v] == 1) {
int tmpA = setA[u];
int tmpB = setB[v];
setA[u] = 1;
setB[v] = 1;
return recurrsive(graph, i, j + 1);
}
if (setA[v] == 1 || setB[u] == 1) {
int tmpA = setA[v];
int tmpB = setB[u];
setA[v] = 1;
setB[u] = 1;
return recurrsive(graph, i, j + 1);
}
int[] tmpSetA = Arrays.copyOf(setA, graph.length);
int[] tmpSetB = Arrays.copyOf(setB, graph.length);
setA[u] = 1;
setB[v] = 1;
if (recurrsive(graph, i, j + 1))
return true;
setA = Arrays.copyOf(tmpSetA, graph.length);
setB = Arrays.copyOf(tmpSetB, graph.length);
setA[v] = 1;
setB[u] = 1;
return recurrsive(graph, i, j + 1);
}
public boolean isBipartite(int[][] graph) {
setA = new int[graph.length];
setB = new int[graph.length];
return recurrsive(graph, 0, 0);
}
}
BFS解法
class Solution {
public boolean isBipartite(int[][] graph) {
int[] color = new int[graph.length];
List<Integer> queue = new ArrayList<>();
for (int i = 0; i < graph.length; i++){
if (color[i] != 0)
continue;
queue.add(i);
color[i] = 1;
while (queue.size() > 0){
int u = queue.get(0);
int thisLayerColor = -color[u];
int[] children = graph[u];
for (int child: children){
if (color[child] == -thisLayerColor)
return false;
if (color[child] == 0){
color[child] = thisLayerColor;
queue.add(child);
}
}
queue.remove(0);
}
}
return true;
}
}