2020年7月16日 判断二分图 isBipartite
默认格式:
class Solution {
public boolean isBipartite(int[][] graph) {
}
}
解题思路:
这道题比较抽象,我们先尽可能多地挖掘信息。
graph[i]表示的是和i相连的点的值。
比如题中的graph[0]={1,2,3}表示0和1,2,3相连
所有的数都是连续的,并且不会出现重复。
问题1:
什么叫做分割为两个独立的子集。也就是所谓的二分图。
特点有以下两点。
每一条边的两端,分别在两个独立的子集中,子集中存的是部分的节点的集合,两个子集加起来就是所有节点的集合。
也许这么说会比较清楚:
我们先看示例1:
通过graph[0]={1,3}我们知道,0和3有一条边,0和1也有一条边,所以我们可以确定0和1、3不是在同一个子集中。
继续看graph[1]={0,2},我们知道了1和0不在同一个子集中,同时,1和2也不在同一个子集中,所以,目前我们可以知道1和3在同一个子集中,0和2在一个子集中,不过这还没完,因为我们没有遍历完所有的节点,我们需要遍历所有节点,确定后面不会出现矛盾的选项才可以(实际上不需要,如果2或者3和其他节点有相连,比如2和0有相连,那么在0中就必定存在2,而0的连接点没有2,所有可以推出2和0不相连)
然后看一个示例2:
通过graph[0]={1,2,3}我们知道,0和1,2,3都是相连的,所以,如果要分子集,只能是{0},{1,2,3},但是看第二个,graph[1]={0,2}我们又发现1和2也是相连的,所以1和2不能在同一个子集中,这样和之前的结论就产生矛盾了,说明这不是一个二分图。
实现:
暴力实现,使用逆推的方式,首先遍历所有一级数组,得到和i相连的所有点,把i放入分组a中,把和i相连的放入分组b中。每一个数放入之前先判断另一个分组中是否已经有存在,如果存在直接返回false证明二分图不能构成。
当然,分组存数据用的是HashSet,判断是否存在比较方便。
发现问题:
首先,如果是这样的开始两个数组graph[0]={2,3},graph[1]={4,5},这种情况下如何确定1和0在那个分组内呢?因为没有办法确定,所以不能轻易给0和1分组。否则会在后续的判断中误认为是无法组成二分图的。
其次,如果是空的比如graph[0]={},那么0被分在哪一组都是无所谓的。
第三,可能存在两组不相连的图
这种情况下,第一个子图数之间的关系和第二个子图没有关系。可以独立计算。
改进,将遍历变为递归。
问题2:
由于已经知道所有会出现的数字,所以可以使用数组来代替hashset,下标表示位置,值表示属于哪一组。
int[] array;
public boolean isBipartite(int[][] graph) {
array=new int[graph.length];
for (int i=0;i<graph.length;i++){
//对他进行分组,如果分组出现问题,返回false
if (!each(graph,i )){
return false;
}
}
return true;
}
//遍历,输入数字i就可以把i和其所相连的分组
public boolean each(int[][] graph,int i){
//如果i在组B中
if (array[i]==2){
//把和i相连的都放到A组
for (int j=0;j<graph[i].length;j++){
//如果已经在B组了,报错
if (array[graph[i][j]]==2){
return false;
}
//如果没有分组,对这个数进行递归分组
else if (array[graph[i][j]]==0){
array[graph[i][j]]=1;
each(graph, graph[i][j]);
}
//如果这个数已经在B组了,直接跳过
}
}
//如果在A组或者不存在,两组都不在,随便放到那一组中
else
{
//放到A组
array[i]=1;
//把和i相连的都放到B组
for (int j=0;j<graph[i].length;j++){
//如果已经在A组了,报错
if (array[graph[i][j]]==1){
return false;
}
//如果没有分组,对这个数进行递归分组
else if (array[graph[i][j]]==0){
array[graph[i][j]]=2;
each(graph, graph[i][j]);
}
//如果这个数已经在B组了,直接跳过
}
}
return true;
}