判断无向图是否连通
- DFS,BFS遍历图,是否所有顶点都能访问到。
- 并查集。
判断图中是否有环
有向图:有向图中的环特指同向的环
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsVSlpDr-1584972787667)(http://note.youdao.com/noteshare?id=d98bba32026574ec204adac734ccdaf7&sub=4699EAF86DEF463A8BE50FC418E4108A)]
1、拓扑排序
//邻接表版
class Solution{
public:
bool topologicalSort(vector<vector<int> >& graph){
int V = graph.size();
int in_degree[V] = {
};
for(vector<int> v:graph){
for(int x:v) in_degree[x]++;
}
//topo
int num = 0; //拓扑排序成功的顶点个数
queue<int> q;
for(int i=0; i<V; i++){
if(in_degree[i]==0) q.push(i);
}
while(!q.empty()){
int v = q.front();
q.pop();
cout<<v<<' ';
num++;
for(int u:graph[v]){
in_degree[u]--;
if(in_degree[u]==0) q.push(u);
}
}
if(num==V) return true;
else return false;
}
};
2、tarjan算法(本质上是DFS)
有向图中的强连通分量要么是一个环,要么是一个点。tarjan
//邻接矩阵版
class Solution{
public:
int V, time; //顶点个数,时间戳
vector<int> dfn, low;
vector<bool> in_stack; //记录顶点是否在栈内
stack<int> s;
vector<vector<int> > tarjan(vector<vector<int> >& graph){
//init
V = graph.size();
dfn.resize(V, 0); //dfn[i]=0还可表示i还未访问到
low.resize(V, 0);
in_stack.resize(V, false);
time = 1;
vector<vector<int> > ans;
//tarjan
for(int i=0; i<V; i++){
if(dfn[i]==0) DFS(i, graph, ans);
}
return ans;
}
void DFS(int i, vector<vector<int> >& graph, vector<vector<int> >& ans){
s.push(i);
in_stack[i] = true;
dfn[i] = low[i] = time++;
for(int j; j<V; j++){
if(graph[i][j]){
if(dfn[j]==0){
DFS(j, graph, ans);
low[i] = min(low[i], low[j]);
}
else if(in_stack[j]) low[i] = min(low[i], dfn[j]);
}
}
if(dfn[i]==low[i]){
vector<int> temp;
int x;
do{
x = s.top();
s.pop();
temp.emplace_back(x);
}while(x!=i);
ans.emplace_back(temp);
}
return;
}
};
无向图
1、n算法
//邻接矩阵版
class Solution{
public:
bool nAlgorithm(vector<vector<int> >& graph){
int V = graph.size();
int degree[V] = {
};
for(int i=0; i<V; i++){
for(int j=0; j<V; j++){
if(graph[i][j]!=0){
degree[i]++;
degree[j]++;
}
}
}
//把所有度<=1的顶点入队,只要入过队,就一定不在环里
queue<int> q;
int num = 0; //入过队的顶点数
for(int i=0; i<V; i++){
if(degree[i]<=1) q.push(i);
}
while(!q.empty()){
int v = q.front();
q.pop();
num++;
for(int j=0; j<V; j++){
if(graph[v][j]!=0){
degree[v]--;
degree[j]--;
if(degree[j]==1) q.push(j);
}
}
}
if(num<V) return true; //有环
else return false;
}
};
2、并查集
并查集一般用于处理树结构,树的边可视为有向边。
处理图时只能压缩路径,原始路径就丢失了,因此只能判断环的存在,不能输出环。
每遇到一条边,就union一次。如果存在环,那么处理到环封口的边上时,两个顶点的father相同。
//邻接矩阵版
class Solution{
public:
vector<int> father; //father存原始路径
bool cyclic(vector<vector<int> >& graph){
int V = graph.size();
father.resize(V);
for(int i=0; i<V; i++) father[i] = i;
for(int i=0; i<V; i++){
for(int j=0; j<V; j++){
if(graph[i][j]!=0){
if(!union_set(i, j)) return true; //有环
}
}
}
return false;
}
int findFather(int a){
int t = a;
while(a!=father[a]) a = father[a];
while(t!=father[t]){
int u = father[t];
father[t] = a;
t = u;
}
return a;
}
bool union_set(int a, int b){
int fa=findFather(a), fb=findFather(b);
if(fa!=fb){
father[fa] = fb;
return true;
}
else return false; //出现环
}
};
3、DFS