算法導論-第22章-基本的圖算法-22.5 強連通分量

轉自

#include <iostream>
using namespace std;

//8個點
#define N 8 
#define WHITE 0
#define GRAY 1
#define BLACK 2


//邊結點結構
struct Edge
{
    int start;//有向圖的起點
    int end;//有向圖的終點
    Edge *next;//指向同一個起點的下一條邊
    int type;//邊的類型
    Edge(int s, int e):start(s),end(e),next(NULL){}
};
//頂點結點結構
struct Vertex
{
    int id;
    Edge *head;//指向以該頂點爲起點的下一條邊
    int color;//頂點的顏色
    Vertex *p;//指向遍歷結果的父結點
    int d, f;//第一次被發現的時間和結束檢查的時間
    Vertex( ):head(NULL),color(WHITE),p(NULL),d(0x7fffffff),id(0){}
};
//圖結構
struct Graph
{
    Vertex V[N+1];//N個頂點
    Graph()
    {
        int i;
        for(i = 1; i <= N; i++)
            V[i].id=i;
    }
};

int time = 0;
bool flag = 0;//flag爲0表示第一次調用DFS,爲1表示第二次調用DFS
int Sort[N+1] = {N};//存儲按u.f從大到小排序時的點,即sort[1]存儲的點的f值最大,sort[N]存儲的點的f值最小,按這個點的順序調用DFS_Visit函數

//插入邊,按邊的end由小到大排列
void InsertEdge(Graph *G, int start , int end)
{
    Edge *E = new Edge(start,end);
    //如果當前點E->start的鏈表爲空,則把E邊作爲head
    if(G->V[E->start].head == NULL)
        G->V[E->start].head =E;
    //如果有,加入到鏈表中,遞增順序排列,便於查重
    else
    {
        //鏈表的插入
        Edge *e1 = G->V[E->start].head, *e2 = e1;
        while(e1 && e1->end < E->end)
        {
            e2 = e1;
            e1 = e1->next;
        }
        //插入了重複的邊,直接返回
        if(e1 && e1->end == E->end)
            return;
        //第一條邊的end都比E邊的end大, 此時e1 == e2,則把E作爲head
        if(e1 == e2)
        {
            E->next = e1;
            G->V[E->start].head =E;
        }
        //找到E的正確位置
        else
        {
            e2->next = E;
            E->next = e1;
        }
    }
}
//轉置,重新構造一個圖
Graph* Reverse(Graph *G)  
{  
    Graph *ret = new Graph;  
    int i;  
    //遍歷圖G中的每一條邊,以終點爲起點,以起點爲終點,加入到新圖RET中   
    for(i = 1; i <= N; i++)  
    {  
        Edge *E = G->V[i].head;  
        while(E)  
        {   
            InsertEdge(ret, E->end, E->start);  
            E = E->next;  
        }  
    }  
    return ret;  
}
//訪問某頂點
void DFS_Visit(Graph *G, Vertex *u)
{
    //在第二次DFS調用時輸出
    if(flag)
        cout<<u->id<<' ';
    //將u置爲黑色
    u->color = GRAY;
    //使全局變量time增值
    time++;
    //將time的新值記錄爲發現時間
    u->d = time;
    //檢查和u相鄰的每個頂點v
    Vertex *v;
    Edge *e = u->head;
    while(e)
    {
        v = &G->V[e->end];
        //如果頂點爲白色
        if(v->color == WHITE)
        {
            //遞歸訪問頂點
            v->p = u;
            DFS_Visit(G, v);
            //樹邊
            e->type = 1;
        }
        else if(v->color == GRAY)
        {
            //反向邊
            e->type = 2;
        }
        else if(v->color == BLACK)
        {
            //正向邊
            if(u->d < v->d)
                e->type = 3;
            //交叉邊
            else
                e->type = 4;
        }
        e = e->next;
    }
    //以u爲起點的所有邊都被探尋後,置u爲黑色
    u->color = BLACK;
    //並將完成時間記錄在f[u]中
    time++;
    u->f = time;
    //把結果按照f從大到小的順序保存於Sort數組中
    if(flag == 0)
    {
        Sort[Sort[0]] = u->id;
        Sort[0]--;
    }
}
//深度優先搜索
void DFS(Graph *G)
{
    int i;
    //對每個頂點初始化
    for(i = 1; i <= N; i++)
    {
        G->V[i].id = i;
        G->V[i].color = WHITE;
        G->V[i].p = NULL;
    }
    //時間戳初始化
    time = 0;
    //依次檢索V中的頂點,發現白色頂點時,調用DFS_Visit訪問該頂點
    for(i = 1; i <= N; i++)
    {
        int j;
        //第一次是以正常順序按點1->2->3->.....->8的順序調用DFS_Visit函數
        if(flag == 0)
            j = i;
        //第二次是以f從大到小的順序,這個順序在第一次dfs次保存於Sort數組中
        else 
            j = Sort[i];
        //發現白色頂點時,調用DFS_Visit訪問該頂點
        if(G->V[j].color == WHITE)
        {
            if(flag)
                cout<<"強連通分量爲:";
            DFS_Visit(G, &G->V[j]);
            //flag == 1時,第二次調用DFS,此時每次調用DFS_Visit 就會輸出一個強連通分量,換行後,再調用,又輸出一個強連通分量
            if(flag)
                cout<<endl;
        }
    }
}
void Strongly_Connected_Component(Graph *G)
{
    //第一次DFS,計算每個頂點的f
    DFS(G);
    //轉置,計算GT   
    Graph *G2 = Reverse(G);  
    //第一次的DFS和第二次的DFS不同,用flag區分
    flag = 1;
    //第二次的DFS,按照f從大到小的順序調用
    DFS(G2);
}
/*
  1  →2→ 3 ↔ 4
↓↑ ↙↓  ↓  ↓
  5 → 6 ↔ 7 →8

  上面8還要指向自己,共15條邊
*/
int main()  
{  
    //構造一個空的圖   
    Graph *G = new Graph;   
    //15條邊
    int edge[16][2] = {{0,0},{1,2},{1,5},{2,3},{2,5},{2,6},{3,4},{3,7},{4,3},{4,8},{5,1},{5,6},{6,7},{7,6},{7,8},{8,8}};
    for(int i = 1; i <= 15; i++)  
    {       
        int start = edge[i][0];
        int end = edge[i][1];  
        InsertEdge(G, start , end);     
    }  
    //計算強聯通分量
    Strongly_Connected_Component(G);
    return 0;  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章