判斷無向圖/有向圖中是否存在環


  本文主要針對如何判斷有向圖/無向圖中是否存在環的問題進行簡單的論述。

一 無向圖

1.利用DFS進行判斷

利用DFS判斷有向圖是否存在環,是最為常用的一種方法,雖然這種方法很常用,但可參考的代碼的實現比較少,下面對這種方法及其實現進行詳細的闡述。

首先,利用DFS判斷無向圖中是否換的原理是:若在深度優先搜索的過程中遇到回邊(即指向已經訪問過的頂點的邊),則必定存在環。

所以說,是否存在環的關鍵在於是否存在滿足條件的“回邊”,那么如何判斷回邊呢?

(1)首先,對圖中的所有頂點定義三種狀態:頂點未被訪問過頂點剛開始被訪問頂點被訪問過並且其所有鄰接點也被訪問過。這三種狀態,在visited數組中分別用0、1、2來表示。那么,存在環的情況可以定義為:在遍歷過程中,發現某個頂點的一條邊指向狀態1的頂點,此時就存在環。狀態2可以理解為其生成樹上的所有的子孫節點都已經訪問完

(2)此外,我們要定義一個father數組,用以存儲在DFS過程中頂點的父頂點(或者說是生成樹上的父節點)。其主要作用是為了區分鄰接點中環中的頂點和遍歷過程中的父節點 (單純的用visited數組無法區分)。

整個過程的實現代碼如下:

 

#define MAX_NUM 100
#define INF 0x7fffffff
/*DFS判斷無向圖中是否有環*/
class Graph
{
    public:
    int vertexNum;//頂點個數
    int arcNum;//弧的個數
    int vertex[MAX_NUM];//頂點表
    int arc[MAX_NUM][MAX_NUM];//弧信息表
};
int visited[MAX_NUM];//頂點訪問表
int father[MAX_NUM];//父節點表
void DFS(int v,Graph G)
{
    visited[v] = 1;
    for(int i = 0 ; i < G.vertexNum; i++)
    {
        if(i != v && G.arc[v][i] != INF)//鄰接矩陣中節點v的鄰接點
        {
            if(visited[i] == 1 && father[v] != i)//vi不是父節點,而且還訪問過(而且為狀態1,說明不是回溯過來的頂點),說明存在環(判斷i不是v的父節點)
            {
                cout<<"圖存在環";
                int temp = v;
                while(temp != i)
                {
                    cout<<temp<<"<-";//輸出環
                    temp = father[temp];
                }
                cout<<temp<<endl;
            }
            else
                if(visited[i] == 0)
                {
                    father[i] = v;//更新father數組
                    DFS(i,G);
                }

        }
    }
    visited[v] = 2;//遍歷完所有的鄰接點才變為狀態2
}
void DFSTraverse(Graph G)
{
    memset(visited,0,sizeof(visited));
    memset(father,-1,sizeof(father));
    for(int i = 0 ; i < G.vertexNum; i++)
        if(!visited[i])
            DFS(i,G);
}

 

  

 

 由此可見,visited數組相對於一般的情況,增加了個狀態2,主要是為了防止在回溯過程中進行誤判。所以才能僅用father數組和狀態1判斷存在環。

狀態2可以理解為其生成樹上的所有的子孫節點都已經訪問完

由於使用的是鄰接矩陣來存儲,所以該算法的時間復雜度為O(n^2),空間復雜度為O(n)。

2.其他方法本文不再詳述。

二 有向圖

1.拓撲排序

關於拓撲排序,資料很多,本文不再詳述其原理,只給出其實現代碼,代碼如下:

 

#include<iostream>
#include<unordered_map>
#include<queue>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<sstream>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define MAX_NUM 100
#define INF 0x7fffffff
/*拓撲排序*/
int indegree[MAX_NUM];//用以表示每個頂點的入度
bool visited[MAX_NUM];//用以表示該頂點是否入棧
class Graph
{
public:
    int vertexNum;//頂點個數
    int arcNum;//弧的個數
    int vertex[MAX_NUM];//頂點表
    int arc[MAX_NUM][MAX_NUM]= {{0,1,1},{INF,0,1},{INF,INF,0}}; //弧信息表
};
void Initindegree(Graph G)//初始化入度數組
{
    memset(indegree,0,sizeof(indegree));
    for(int i = 0; i < G.vertexNum; i++)
        for(int j = 0; j < G.vertexNum; j++)
        {
            if(i != j && G.arc[i][j] != INF)
                indegree[j]++;//注意此處增加的是頂點vj的入度
        }
    memset(visited,0,sizeof(visited));
}
bool TuoPu(Graph G)
{
    stack<int> s;
    int cnt = 0;//用於記錄拓撲序列中節點的個數
    for(int i = 0 ; i < G.vertexNum; i++)
        if(indegree[i] == 0)
        {
            s.push(i);
            visited[i] = true;//修改入棧頂點的入棧標記數組

        }
    while(!s.empty())
    {
        int v = s.top();
        cnt++;//頂點出棧得到時候,計數器加1
        s.pop();

        for(int i = 0; i < G.vertexNum; i++)
        {
            if(v != i && G.arc[v][i] != INF && visited[i] == false)//將所有頂點v的未入棧的鄰接點的入度都減去1
            {
                indegree[i]--;
                if(indegree[i] == 0)//如果減1后入度為0了,此時需要將該鄰接點入棧,且修改入棧標記數組
                {
                    visited[i] = true;
                    s.push(i);
                }
            }


        }
    }
    return cnt == G.vertexNum ? true : false;
}
int main()
{
    Graph G;
    G.vertexNum = 3;
    Initindegree(G);
    cout<<TuoPu(G)<<endl;

}

 

  

 

2.利用改進的DFS

對於有向圖的話,如果直接應用一般的DFS的話,會出現誤判的情況,一個典型的例子是:A->B,A->C->B,我們用DFS來處理這個圖,我們會得出它有環,但實際上並沒有。然而,本文中所說的無向圖的DFS判斷算法完全可以直接應用到有向圖中來,即上述代碼可以直接應用到有向圖中來。所以說上述的DFS算法(或稱為為改進的DFS算法)既適用於無向圖,也適用於有向圖。其對應的原理適用於這兩種圖,即只要我們在遍歷過程中,只要發現一個頂點不是當前節點的父節點,同時他還被訪問過了(狀態為1),那么就可以認為此處存在環。(通常在DFS中一個頂點的未被訪問的鄰接點,相當於生成樹中的該頂點的子孫節點)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM