题目
给定一个n个点m条边的无向图,图中可能存在重边和自环。
请你判断这个图是否是二分图。
输入格式
第一行包含两个整数n和m。
接下来m行,每行包含两个整数u和v,表示点u和点v之间存在一条边。
输出格式
如果给定图是二分图,则输出“Yes”,否则输出“No”。
数据范围
1≤n,m≤105
输入样例:
4 4
1 3
1 4
2 3
2 4
输出样例:
Yes
算法分析
二分法其实跟遍历算法有很大的结合关系.对于一个连通域的来说,遍历算法都是可以全部遍历,而在遍历的过程中为每一个节点交叉染色,就实现了二分染色.一旦发现应染为1色的结点被染为2色那就说明无法进行二分.也就是说,起一个头其实是可以一起染出一块连通域的. 但是由于图内可能是有很多个连通域,因此我们先随便找一个结点染色(此时他属于的连通域全被染色,如果没有问题的话),再遍历其他未染色的结点,重新染色,这样其他连通域也会被逐渐染色.到最后,其实不会有没有被染色的结点,只有染色失败(重复)的报错,从而判定是否是二分图.
源代码
import java.util.*;
class Main{
static int N=100010;
static int M=200010;
static int index=0;
static int[] h=new int[N];
static int[] e=new int[M];
static int[] ne=new int[M];
static int[] color=new int[N];
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
while(m-->0){
int u=sc.nextInt();
int v=sc.nextInt();
add(u,v);
add(v,u);
}
boolean flag=true;
for(int i=1;i<=n;i++){
if(color[i]==0) {
if(!dfs(i,1)) {
flag=false;
break;}
}
}
if(flag) System.out.print("Yes");
else System.out.print("No");
}
public static void add(int u,int v){
e[++index]=v;
ne[index]=h[u];
h[u]=index;
}
public static boolean dfs(int u,int c){
color[u]=c;
for(int i=h[u];i!=0;i=ne[i]){
int j=e[i];
if(color[j]==c) return false ;
else if(color[j]==0){
if(!dfs(j,3-c)) return false;
}
}
return true;
}
}
代码特别说明
- 对于无向图,添加时要添加两遍,同时,在设定边的大小时也要为给定边数的两倍
- 对于关键代码的思路解释:
首先确定函数dfs(u,c)的含义是给节点u染色为c,并以此染其连通域所有的点,成功返回true,失败返回false,步骤:
- 给当前节点染色
- 找到其邻节点,邻节点有两个状态
- 没有被染色: 那么此时就以这个节点为头来染色(满足邻节点不同色的条件),失败返回true
- 被染色了:那就看看这个被染色的结点颜色是不是我们期待的,不是则返回false
- 当所有结点全部遍历结束,并且没有中途返回false,那么就说明这个连通域染色成功,返回false
- 因为我们需要对邻节点进行操作,所以用邻接表存储
- 最后判定的时候,如前面所说,只需要找到没有染色的结点来对其连通域染色即可.