- https://leetcode.cn/problems/is-graph-bipartite/
- 判断给定的图是否为二分图,如果图是二分图,返回 true ,否则,返回 false 。
- 二分图 定义:如果能将一个图的节点集合分割成两个独立的子集 A 和 B ,
并使图中的每一条边的两个节点一个来自 A 集合,一个来自 B 集合
,就将这个图称为 二分图 。
存在一个 无向图 ,图中有 n 个节点。其中每个节点都有一个介于 0 到 n - 1 之间的唯一编号。给你一个二维数组 graph ,其中 graph[u] 是一个节点数组,由节点 u 的邻接节点组成。形式上,对于 graph[u] 中的每个 v ,都存在一条位于节点 u 和节点 v 之间的无向边。该无向图同时具有以下属性:
-
不存在自环(graph[u] 不包含 u)。[一般邻接表表示的特点]
-
不存在平行边(graph[u] 不包含重复值)。[一般邻接表表示的特点]
-
如果 v 在 graph[u] 内,那么 u 也应该在 graph[v] 内(该图是无向图)[一般邻接表表示的特点]
-
这个图可能不是连通图,也就是说两个节点 u 和 v 之间可能不存在一条连通彼此的路径。
输入:graph = [[1,3],[0,2],[1,3],[0,2]]
输出:true
解释:可以将节点分成两组: {0, 2} 和 {1, 3} 。//子集各自几点没有相连
输入:graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
输出:false
解释:不能将节点分割成两个独立的子集,以使每条边都连通一个子集中的一个节点与另一个子集中的一个节点。
题解
并查集
我们知道如果是二分图的话,那么图中每个顶点的所有邻接点都应该属于同一集合,且不与顶点处于同一集合。因此我们可以使用并查集来解决这个问题,我们遍历图中每个顶点,将当前顶点的所有邻接点进行合并,并判断这些邻接点中是否存在某一邻接点已经和当前顶点处于同一个集合中了,若是,则说明不是二分图。
#include <iostream>
#include<vector>
#include<memory>
using namespace std;
class Solution_UnionSet {
public:
bool res = true;//是否为二分图
vector<int> fa;// 并查集
int find(int val){
if(fa[val] == val){
return val;
}else{
return find(fa[val]);
}
}
void merge(int x,int y){
int fa_x = find(x);
int fa_y = find(y);
fa[fa_x] = fa_y;
}
bool isBipartite(vector<vector<int>>& g) {
fa.resize(g.size());
//并查集数组初始化
for(int index=0;index<g.size();index++){
fa[index] = index;
}
for(int i = 0; i < g.size(); i++){
for(int v:g[i]){
merge(g[i][0],v);//将当前节点的邻接节点划为一类
if(find(i)==find(v)){//如果当前节点和邻接节点在一个集合中
res = false;
return false;
}
}
}
return res;
}
};
//reserve和 resize的区别,resize可以赋默认值,reverse只是分配了空间,不能进行a[i]的访问,需要依次push_back()
int main(){
unique_ptr<Solution_UnionSet> mysolo = unique_ptr<Solution_UnionSet>(new Solution_UnionSet());
vector<vector<int>> v {{1,2,3},{0,2},{0,1,3},{0,2}};
//vector<vector<int>> v {{1,3},{0,2},{1,3},{0,2}};
bool res = mysolo->isBipartite(v);
cout<< res;
}
//原版java代码来自链接:https://leetcode.cn/problems/is-graph-bipartite/solution/bfs-dfs-bing-cha-ji-san-chong-fang-fa-pan-duan-er-/
遍历染色
深度优先搜索 / 广度优先搜索
- 我们使用图搜索算法从各个连通域的任一顶点开始遍历整个连通域,遍历的过程中用两种不同的颜色对顶点进行染色,相邻顶点染成相反的颜色。这个过程中倘若发现相邻的顶点被染成了相同的颜色,说明它不是二分图;反之,如果所有的连通域都染色成功,说明它是二分图。
#include <iostream>
#include<vector>
#include<memory>
using namespace std;
class Solution {
public:
bool res = true;//是否为二分图
vector<bool> color;// 记录图中节点的颜色,false 和 true 代表两种不同颜色
vector<bool> visited;// 记录图中节点是否被访问过
bool isBipartite(vector<vector<int>>& graph) {
color.resize(graph.size(),false);
visited.resize(graph.size(),false);
// ⭐因为图不一定是联通的,可能存在多个子图
// 所以要把每个节点都作为起点进行一次遍历
// 如果发现任何一个子图不是二分图,整幅图都不算二分图
for(int i = 0; i < graph.size(); i++){
if(!visited[i]){
dfs(graph, i);
}
}
return res;
}
// DFS 遍历
void dfs(vector<vector<int>> graph, int v){
if(!res) return; // ⭐优化:如果结果已经不是二分图了,就无需再继续遍历
visited[v] = true;
for(int i : graph[v]){
if(!visited[i]){// 相邻节点 i 没有被访问过
color[i] = !color[v]; // 那么应该给节点 i 涂上和节点 v 不同的颜色
dfs(graph, i);
}else{// 相邻节点 i 已经被访问过
if(color[i] == color[v]){// 如果 v 和 i 的颜色相同则不是二分图
res = false;
}
}
}
}
};
//revese 和 resize的区别,resize可以赋默认值,reverse只是分配了空间,不能进行a[i]的访问,需要依次push_back()