相关概念:
- 把相邻顶点染成不同颜色的问题叫做图的着色问题。
- ➢对图进行染色所需的最小颜色数,称为最小着色数。最小着色数为2的图称为二分图。
- ➢二分图:给定一个具有n个顶点的图,要给图上每个顶点染色,并且要使相邻的顶点的颜色不同、问是否最多用2种颜色进行染色?没有重边(相邻的两个顶点有两条边)和自环。
解题思路:
用邻接表进行对图的表示,这样方便查找点的相邻顶点。深搜遍历每一个点,依次尝试每一种颜色;在深搜的过程中记录已标记的颜色。
代码如下:
邻接表类:
import java.util.ArrayList;
import java.util.List;
/*
* 邻接表的头结点
*/
public class GraphNodeAl {
public int val; //表示节点的位置
public List<GraphNodeAl> neighbors; //节点所在的链表,存储所有与该节点相邻的节点
public GraphNodeAl(int val){
this.val = val;
}
//获取与节点相邻的节点i
public GraphNodeAl getNeighbor(int i){
return neighbors.get(i);
}
//添加与节点相邻的节点
public void add(GraphNodeAl node){
if(this.neighbors == null){
this.neighbors = new ArrayList<GraphNodeAl>();
}
neighbors.add(node);
}
//返回与该节点相邻的节点个数
public int size(){
return this.neighbors.size();
}
}
解题代码:
import 图的表示.GraphNodeAl;
/*
* 判断给定的图是否是二分图
*/
public class 二分图 {
public static void main(String[] args) {
//构建图的节点,这里从1开始计数
MyNode n1 = new MyNode(1);
MyNode n2 = new MyNode(2);
MyNode n3 = new MyNode(3);
MyNode n4 = new MyNode(4);
n1.add(n2);
n1.add(n4);
n2.add(n1);
n2.add(n3);
n3.add(n2);
n3.add(n4);
n4.add(n1);
n4.add(n3);
//从任意的顶点开始遍历都可以
System.out.println(dfs(n1,1));
}
//遍历图的顶点,判断是否能构成二分图, 用1和2表示两种颜色,0表示还没有上色
private static boolean dfs(MyNode n1, int c) {
n1.color = c;
//依次尝试对相邻的顶点进行深搜
for(int j=0; j<n1.size(); j++){
MyNode nei = (MyNode)n1.getNeighbor(j);
if(nei.color==c){ //与相邻的节点颜色相同
return false;
}
if(nei.color==0){ //没有颜色
if(!dfs(nei, f(c))){ //给邻居标记相反的颜色
return false;
}
}
}
return true;
}
//取相反的颜色
private static int f(int c) {
if(c==1){
return 2;
}
return 1;
}
}
class MyNode extends GraphNodeAl{
public int color;
public MyNode(int val) {
super(val);
}
public MyNode(int val, int color){
super(val);
this.color = color;
}
}
解法二:
上面的解法是深搜遍历图的方式解决的,相当于模拟在图中行走标记不同的颜色。
我们其实也可以不把它当成一个图,然后深搜回溯的方式填充颜色。也就是说按照顺序给每一个节点依次标记颜色,每标记一个节点的颜色就进行一次判断看是否有相邻的节点发生颜色的冲突。
如果有相邻的节点发生颜色的冲突,那就回溯,退回上一个节点尝试其他颜色。
从深搜树过程上看,这样就会形成一个二叉树,因为每一个节点都有两种颜色的选择。
代码如下:
import 图的表示.GraphNodeAl;
/*
* 判断给定的图是否是二分图
*/
public class 二分图解法二 {
private static MyNode[] arr = new MyNode[5];
public static void main(String[] args) {
//构建图的节点,这里从1开始计数
arr[1] = new MyNode(1);
arr[2] = new MyNode(2);
arr[3] = new MyNode(3);
arr[4] = new MyNode(4);
arr[1].add(arr[2]);
arr[1].add(arr[4]);
arr[2].add(arr[1]);
arr[2].add(arr[3]);
arr[3].add(arr[2]);
arr[3].add(arr[4]);
arr[4].add(arr[1]);
arr[4].add(arr[3]);
//从任意的顶点开始遍历都可以
System.out.println(dfs(arr,1));
}
private static boolean dfs(MyNode[] arr, int i) {
for(int j=1; j<=2; j++){
if(check(arr,i,j)){
arr[i].color = j;
if(i==arr.length-1){ //出口
return true;
}
return dfs(arr, i+1);
}
}
return false;
}
//判断节点i相邻的节点是否有颜色冲突
private static boolean check(MyNode[] arr, int i, int j) {
//遍历相邻的节点,对比颜色
for(int k=0; k<arr[i].size(); k++){
MyNode temp = (MyNode)arr[i].getNeighbor(k);
if(j==temp.color){
return false;
}
}
return true;
}
}