给定一个无向图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
我们不能将节点分割成两个独立的子集。
思路:对于图中的任意两个节点 uu 和 vv,如果它们之间有一条边直接相连,那么 uu 和 vv 必须属于不同的集合
class Solution {
//二分图
//可能存在非联通图情况
public boolean isBipartite(int[][] graph) {
if(graph.length==0) return true;
int[][] array=new int[graph.length][graph.length];
int[] visited=new int[graph.length];
for(int i=0;i<graph.length;i++){//邻接表
for(int j=0;j<graph[i].length;j++){
array[i][graph[i][j]]=1;
array[graph[i][j]][i]=1;
}
}
for(int i=0;i<graph.length;i++){
for(int j=0;j<graph.length;j++){
if(array[i][j]==1 && visited[j]==0){//这里为啥是j?
//每一个连通子图,只执行一次,目的是找到一个入口,并把入口初始化
visited[i]=-1;
dfs(array,visited,j,i);
}
}
}
for(int i=0;i<graph.length;i++){
for(int j=0;j<graph.length;j++){
if(array[i][j]==1){
if(visited[i]==visited[j]){
return false;
}
}
}
}
return true;
}
//k表示当前
public void dfs(int[][] array,int[] visited,int k,int old){
visited[k] = visited[old]==1?-1:1;
for(int i=0;i<array.length;i++){
if(array[k][i]==1 && visited[i]==0){
dfs(array,visited,i,k);
}
}
}
}
class Solution {
public boolean isBipartite(int[][] graph) {
int N = graph.length;
int[] status = new int[N]; // 0 - 1 1
for(int i = 0; i < N; i++){
if(status[i] == 0){
if(!dfs(i, status, graph, -1)){
return false;
}
}
}
return true;
}
public boolean dfs(int index, int[] status, int[][] graph, int preStatus){
if(status[index] != 0){
return status[index] == -1 * preStatus;
}
status[index] = -1 * preStatus;
for(int next : graph[index]){
if(!dfs(next, status, graph, status[index])){
return false;
}
}
return true;
}
}
并查集
class Solution {
public boolean isBipartite(int[][] graph) {
// 初始化并查集
UnionFind uf = new UnionFind(graph.length);
// 遍历每个顶点,将当前顶点的所有邻接点进行合并
for (int i = 0; i < graph.length; i++) {
int[] adjs = graph[i];
for (int w: adjs) {
// 若某个邻接点与当前顶点已经在一个集合中了,说明不是二分图,返回 false。
if (uf.isConnected(i, w)) {
return false;
}
uf.union(adjs[0], w);
}
}
return true;
}
}
// 并查集
class UnionFind {
int[] roots;
public UnionFind(int n) {
roots = new int[n];
for (int i = 0; i < n; i++) {
roots[i] = i;
}
}
public int find(int i) {
if (roots[i] == i) {
return i;
}
return roots[i] = find(roots[i]);
}
// 判断 p 和 q 是否在同一个集合中
public boolean isConnected(int p, int q) {
return find(q) == find(p);
}
// 合并 p 和 q 到一个集合中
public void union(int p, int q) {
roots[find(p)] = find(q);
}
}