求有向圖的強連通分量(c語言版)
有向圖G:
1.選用何種結構儲存有向圖?
選用十字連結串列的結構來儲存圖
2.選用何種遍歷方式?
選用深度優先搜尋
3.思路
3.1 首先從圖G上某個頂點出發沿以該頂點為尾的弧深度優先搜尋,在頂點深度優先搜尋結束之時把該頂點存放到輔助陣列finished中(程式中實際存放的是該頂點在圖中的位置,當然頂點的位置是人為定的,a的位置是0,b的位置是1,c的位置是2,d的位置是3,頂點在圖的結構體中用陣列存起來)。
解釋:假如從a頂點開始深度遍歷,過程是a->c->d,那麼finished陣列的值是 {3,2,0},深度遍歷完後,a頂點的訪問才結束,所以a最後退出這次遍歷,a存放到finished陣列最後。
3.2 然後從最後搜尋完成的頂點(即finished陣列中最後一個元素)出發,沿著以該頂點為頭的弧作逆向的深度優先搜尋遍歷,若以該頂點的這次深度遍歷不能訪問到圖中所有的頂點,就從陣列finished中最後完成搜尋的頂點(還未被訪問的)再次深度優先搜尋遍歷,以此類推,直到圖中所有的頂點都被訪問過。
解釋:假如finished陣列中的值是{1,3,2,0},從0開始逆向的深度優先搜尋,能訪問到2,3,但是1訪問不到,假如1前面還有4,5頂點({4,5,1}),因為陣列finished是按退出深度優先搜尋的順序存進去的,所以1就是當前最後完成深度優先搜尋的頂點(相比4,5頂點),因此再從1頂點開始逆向深度優先遍歷,以此類推,直到所有頂點都訪問到。
3.3 列印出來的結果就是各強連通分量的頂點集。
4.原理
強連通分量的意思是 有向圖G=(V,{A}),(V代表頂點集,A代表弧),如果對於每一對vi,vj屬於V,vi不等於vj,從vi到vj和從vj到vi都儲存路徑,稱G是強連通圖,而有向圖中的極大連通子圖叫有向圖的強連通分量(如vi到vj有路徑,vj到vi也要有路徑,才叫強連通)。根據圖,從a頂點根據以弧尾是a的弧能訪問到d,那麼從a頂點根據以弧頭是a的弧也能訪問到d,說明a到d有路徑,d到a也有路徑,即是通的。
------------------------------------------------------------------------------------------------------------
原始碼地址:git@github.com:hglspace/OlGraph.git
-------------------------------------------------------------------------------------------------------------
myhead.c
#define MAX_VERTEX_NUM 4//人為設定頂點數為4
typedef int Bool;//自定義布林型變數
#define True 1//以訪問狀態
#define False 0//未訪問狀態
//十字連結串列儲存有向圖
struct ArcBox{//弧結點
int tailvex;//尾結點在圖中的位置
int headvex;//頭結點在圖中的位置
struct ArcBox * hlink;//弧頭相同的下一條弧
struct ArcBox * tlink;//弧尾相同的下一條弧
Bool mark;//訪問的標誌 True代表訪問過,False代表沒有訪問過
};
struct VexNode{//頂點結點
char data;//頂點資訊
struct ArcBox * firstin;//以該頂點為弧頭的第一條弧
struct ArcBox * firstout;//以該頂點為弧尾的第一條弧
};
struct OlGraph{
struct VexNode xlist[MAX_VERTEX_NUM];//頂點結點陣列
int vexnum;//當前頂點數量
int arcnum;//當前弧的數量
};
//自定義標頭檔案
-----------------------------------------------------------operateGraph.c
#include <stdio.h>
#include "myhead.h"
#include <stdlib.h>
Bool visits[MAX_VERTEX_NUM];//定義頂點輔助陣列,記錄頂點是否訪問過
int count=0;//定義全域性變數,輔助陣列finished使用
struct ArcBox * edg[MAX_VERTEX_NUM];//定義全域性弧指標陣列,記錄弧地址,方便把弧的是否訪問過的狀態重置
/*
初始化有向圖
*/
struct OlGraph init(void){
int i,j,k,m;
char data1,data2;//用來接受頂點的資訊,根據資訊查詢頂點的位置
struct OlGraph g;
int locateVex(struct OlGraph g,char data);//宣告查詢頂點位置的函式
printf("請輸入圖的頂點數:");
scanf("%d",&g.vexnum);
printf("請輸入圖的邊數:");
scanf("%d",&g.arcnum);
//開始初始化頂點和弧
for(i=0;i<g.vexnum;i++){//初始化頂點
printf("請輸入第%d個頂點:",i+1);
scanf(" %c",&g.xlist[i].data);
g.xlist[i].firstin=NULL;
g.xlist[i].firstout=NULL;
visits[i]=False;
}
for(j=0;j<g.arcnum;j++){//初始化弧
struct ArcBox * p = malloc(sizeof(struct ArcBox));//開闢儲存弧的空間
if(p==NULL){//如果開闢失敗,程式終止
exit(-1);
}
printf("請輸入第%d條邊的起點:",j+1);
scanf(" %c",&data1);
printf("請輸入第%d條邊的終點:",j+1);
scanf(" %c",&data2);
k=locateVex(g,data1);
m=locateVex(g,data2);
p->headvex=m;//對弧的頭結點賦值
p->tailvex=k;//對弧的尾結點賦值
p->hlink=NULL;
p->tlink=NULL;
p->mark=False;//弧的狀態置為未訪問狀態
if(g.xlist[m].firstin==NULL){//m結點是頭結點,需要動態改變該結點第一個以該結點為頭結點的弧
g.xlist[m].firstin=p;
}else{
p->hlink=g.xlist[m].firstin;
g.xlist[m].firstin=p;
}
if(g.xlist[k].firstout==NULL){//k結點是尾結點,需要動態改變該結點第一個以該結點為尾結點的弧
g.xlist[k].firstout=p;
}else{
p->tlink=g.xlist[k].firstout;
g.xlist[k].firstout=p;
}
edg[j]=p;//把弧的指標儲存到 弧指標陣列中
}
return g;
}
/*
根據頂點的資訊查詢頂點在圖中的位置
*/
int locateVex(struct OlGraph g,char data){
int i;
for(i=0;data!=g.xlist[i].data;i++);
return i;
}
/*
生成有向圖的強連通分量的頂點集
*/
void DFSTraverse(struct OlGraph g){
int i,k,s;
int finished[MAX_VERTEX_NUM];//該陣列儲存退出DFS函式的頂點(按照退出的順序,先退出來的頂點先記錄。記錄的是頂點的位置),作用是:方便反向深度遍歷頂點
void DFS(struct OlGraph g,int i,int finished[]);//宣告深度遍歷頂點的函式
void DFSRever(struct OlGraph g,int i,int finished[]);//宣告按照陣列finished中儲存的頂點,從finished[n-1]元素中儲存的頂點開始深度遍歷的函式 (n是儲存的頂點個數)
for(i=0;i<g.vexnum;i++){//開始遍歷頂點
if(visits[i]==True){//如果訪問過,就跳過
continue;
}
DFS(g,i,finished);//呼叫函式
//finished[count++]=i;
}
for(k=0;k<g.vexnum;k++){//把頂點訪問的標誌重置到開始狀態
visits[k]=False;
}
for(s=0;s<g.arcnum;s++){//把弧的訪問標誌重置到開始狀態
edg[s]->mark=False;
}
while(count>=1){//按照finished陣列中存放的頂點,開始深度遍歷
if(visits[finished[count-1]]==True){//如果該頂點訪問過就跳過
continue;
}
visits[finished[count-1]]=True;//置該頂點的訪問狀態為已訪問
printf("%c",g.xlist[finished[count-1]].data);//列印該頂點的資訊,其實就是對該頂點進行操作,訪問
DFSRever(g,finished[count-1],finished);//呼叫函式遍歷
printf("\n.........\n");
count--;//訪問下一頂點
}
}
void DFSRever(struct OlGraph g,int i,int finished[]){
int j;
int getTailAdjVex(struct OlGraph g,int i);//宣告查詢以該頂點為頭的弧的尾頂點的函式
for(j=getTailAdjVex(g, i);j>=0;j=getTailAdjVex(g, i)){
if(visits[j]==True){
continue;
}
visits[j]=True;
DFSRever(g,j,finished);//遞迴呼叫,查詢i結點的鄰接點的鄰接點
printf("%c",g.xlist[j].data);//訪問i結點的鄰接點
}
}
void DFS(struct OlGraph g,int i,int finished[]){
visits[i]=True;
int w;
int getHeadAdjVex(struct OlGraph g,int i);//宣告以該結點為尾的弧的頭結點的函式
for(w=getHeadAdjVex(g,i);w>=0;w=getHeadAdjVex(g,i)){
if(visits[w]==True){
continue;
}
DFS(g,w,finished);//遞迴呼叫,其實就是深度遍歷
}
finished[count++]=i;//退出函式時把該結點儲存到finished陣列中
}
/*
查詢以該結點為尾的弧的頭結點
*/
int getHeadAdjVex(struct OlGraph g,int i){
struct ArcBox * p=g.xlist[i].firstout;
if(p==NULL){
return -1;
}
//for(p=g.xlist[i].firstout;p->mark==True;p=p->tlink);
while(p->mark==True){
p=p->tlink;
if(p==NULL){
return -1;
}
}
p->mark=True;
return p->headvex;
}
/*
查詢以該頂點為頭的弧的尾頂點
*/
int getTailAdjVex(struct OlGraph g,int i){
struct ArcBox * p=g.xlist[i].firstin;
if(p==NULL){
return -1;
}
//for(;p->mark==True;p=p->hlink);
while(p->mark==True){
p=p->hlink;
if(p==NULL){
return -1;
}
}
p->mark=True;
return p->tailvex;
}
main.c
#include <stdio.h>
#include "myhead.h"
int main(int argc, const char * argv[]){
//printf("你好");
struct OlGraph init(void);
void DFSTraverse(struct OlGraph g);
struct OlGraph g=init();
DFSTraverse(g);
return 0;
}
(結語:我主要是做java開發的,為什麼用c語言來寫關於有向圖的操作呢?主要是大學裡學過c,懂一點基礎,為了能更深入瞭解c語言,業餘時間就用c寫點程式練習練習。個人感覺學好c語言有助於理解更高階的如java語言,java的虛擬機器就是有用c語言寫的。第二次記錄點東西到csdn,如有不妥的地方,請幫忙指出。計算機的世界真的很精彩,我還需要努力的探索!加油,各位)
相關文章
- Tarjan 求有向圖的強連通分量
- 圖之強連通、強連通圖、強連通分量 Tarjan演算法演算法
- 有向圖的連通性(判強連通)
- 有向圖的強連通性(java)Java
- 強連通分量
- Tarjan演算法求強連通分量總結演算法
- 【筆記】tarjian演算法 求強連通分量筆記演算法
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- C語言單向連結串列的增刪操作C語言
- C語言實現有向無環圖的拓撲排序演算法C語言排序演算法
- 向C語言致敬C語言
- 雙向連結串列介面設計(C語言)C語言
- 詳解雙向連結串列的基本操作(C語言)C語言
- C語言加強C語言
- 強連通分量(Tarjan演算法)演算法
- 【模板】tarjan 強連通分量縮點
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- 【筆記/模板】無向圖的雙連通分量筆記
- 強連通圖的演算法演算法
- Tarjan演算法(強連通分量分解)演算法
- c語言單向連結串列逆轉實現方法C語言
- 單向迴圈連結串列介面設計(C語言)C語言
- 雙向迴圈連結串列基本操作的實現(C語言)C語言
- 【c語言】求絕對值C語言
- C語言資料結構:雙向連結串列的增刪操作C語言資料結構
- C語言 截圖C語言
- 圖論複習之強連通分量以及縮點—Tarjan演算法圖論演算法
- 強連通分量-tarjan演算法模板詳解演算法
- C語言之雙向連結串列C語言
- C語言之單向連結串列C語言
- C語言 連結串列排序C語言排序
- C語言陣列求學生成績C語言陣列
- C 語言使用非迴圈雙向連結串列實現佇列佇列
- C語言資料結構:單向迴圈連結串列的增刪操作C語言資料結構
- C語言資料結構:雙向迴圈連結串列的增刪操作C語言資料結構
- 最短的崩潰程式(C語言版)C語言
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- 演算法學習之路|強連通分量+縮點演算法