圖
在數學中,圖是描述於一組對象的結構,其中某些對象對在某種意義上是“相關的”。這些對象對應於稱為頂點的數學抽象(也稱為節點或點),並且每個相關的頂點對都稱為邊(也稱為鏈接或線)。通常,圖形以圖解形式描繪為頂點的一組點或環,並通過邊的線或曲線連接。 圖形是離散數學的研究對象之一。 ------百度百科
6.1 基本術語
圖:記為 G=( V, E )
其中:V 是G的頂點集合,是有窮非空集;
E 是G的邊集合,是有窮集
6.2 存儲結構
- 圖的特點:非線性結構
- 圖的順序存儲結構:無(多個定點,無序可言),但可以用數組描述元素間的關系。(設計為鄰接矩陣)
- 鏈式存儲結構:可以用多重鏈表(鄰接表,鄰接多重表、十字鏈表)
下面結論面介紹:圖的數組表示法(鄰接矩陣)和鏈式表示法(鄰接表)
圖的數組表示法(鄰接矩陣為例)
直接給出存儲結構代碼
#define MAX_VERTEX_NUM 100 /*最大頂點數設為100*/
/*---圖的鄰接矩陣表示---*/
typedef struct
{
int vexs[MAX_VERTEX_NUM]; /*頂點向量*/
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*鄰接距陣*/
int vexnum,arcnum; /*頂點數和邊數*/
int kind; /*---圖的類型(1:有向圖 0:無向圖)---*/
}Mgraph;
解釋
給出邊的矩陣表示形式
記住以上規律,對於有向圖和無向圖的創建,有很大幫助
圖的鏈式存儲結構(鄰接表為例)
/*-------------- 圖的鄰接表--------*/
#define MAX_VERTEX_NUM 100
typedef struct ArcNode{
int adjvex; //該弧所指向的頂點位置
struct ArcNode *nextarc; //指向下一條弧的指針
int weight;
}ArcNode; //表結點 (狐)
typedef struct VNode{
int data; //頂點信息
ArcNode *firstarc; //指向第一條依附該頂點的弧的指針
}VNode; //頂點
typedef struct {
Vnode adjlist [MAX_VERTEX_NUM]; /*鄰接表*/
int vexnum,arcnum; //頂點數,邊數
int kind;
}ALGraph; //圖的鄰接表
解釋
6.3 圖的遍歷
遍歷定義:從已給的圖中某一頂點出發,沿着一些邊,訪遍圖中所有的頂點,且 使每個頂點僅被訪問一次,就叫做圖的遍歷。
遍歷實質:找每個頂點的鄰接點的過程。
圖的特點:圖中可能存在回路,且圖的任一頂點都可能與其它頂點相通,在訪問完某個頂點之后可能會沿着某些邊又回到了曾經訪問過的頂點。
解決思路:可設置一個輔助數組 visited [n ],用來標記每個被訪問過的頂點。它的初始狀態為0,在圖的遍歷過程中,一旦某一個頂點i 被訪問,就立即改 visited [i]為1,防止它被多次訪問。
圖的常用遍歷方式:1.深度優先搜索 2.廣度優先搜索
圖的遍歷之深度優先遍歷
深度優先遍歷(Depth First Search),也有稱為深度優先搜索,簡稱為DFS。
其原理如下:
- 在訪問圖中某一起始頂點 v 后,由 v 出發,訪問它的任一鄰接頂點 w1;
- 再從 w1 出發,訪問與 w1鄰接但還未被訪問過的頂點 w2;
- 然后再從 w2 出發,進行類似的訪問,…
- 如此進行下去,直至到達所有的鄰接頂點都被訪問過的頂點 u 為止。
- 接着,退回一步,退到前一次剛訪問過的頂點,看是否還有其它未被訪問的鄰接頂點。
- 如果有,則訪問此頂點,之后再從此頂點出發,進行與前述類似的訪問;
- 如果沒有,就再退回一步進行搜索。重復上述過程,直到連通圖中所有頂點都被訪問過為止
注:作圖做的不好,最好慢理解文字吧
不難從文字中看出,這是一個遞歸思想。
給出代碼(鄰接矩陣):
void DFS( Mgraph g,int v,int visited[])
{/* 鄰接矩陣存儲,從頂點v出發,對圖g進行深度優先搜索*/
int j;
printf("%d ",g.vexs[v]);
visited[v]=1; /*標識v被訪問過*/
for(j=0;j<g.vexnum;j++); /* */
{
if( g.arcs[v][j]==1&&visited[j]==0)/*j為v的鄰接點,未被訪問過*/
DFS(g,j,visited); /*從j出發遞歸調用DFS*/
}/*for*/
}/*DFS*/
void DFSTraverse(Mgraph g)
{ /*鄰接矩陣 深度優先搜索*/
int v;
int visited[MAX_VERTEX_NUM];
for(v=0;v<g.vexnum;v++)
visited[v]=0; /*初始化visited數組*/
for(v=0;v<g.vexnum;v++)
if(visited[v]==0) DFS(g,v,visited);
/*從未被訪問過的v出發,DFS搜索*/
}
給出代碼(鄰接表):
void DFS(ALGraph *g,int v,int visited[])
{/*從頂點v出發,對圖g進行深度優先搜索*/
ArcNode *p;
int w;
printf("%d ",g->adjlist[v].data);
visited[v]=1; /*標識v被訪問過*/
p=g->adjlist[v].firstarc; /*p指向第v個單鏈表的頭指針*/
while(p)
{
w=p->adjvex; /*w為v的鄰接點*/
if(visited[w]==0) /*若w未被訪問*/
DFS(g,w,visited); /*從w出發遞歸調用DFS*/
p=p->nextarc; /*找v的下一個鄰接點*/
}/*while*/
}/*DFS*/
void DFSTraverse(ALGraph *g)
{ /*鄰接表 深度優先搜索*/
int v;
int visited[MAX_VERTEX_NUM];
for(v=0;v<g->vexnum;v++)
visited[v]=0; /*初始化visited數組*/
for(v=0;v<g->vexnum;v++)
if(visited[v]==0) DFS(g,v,visited);
/*從未被訪問過的v出發,DFS搜索*/
}
DFS算法的性能分析
DFS算法是一個遞歸算法,需要借助一個遞歸工作棧,故其空間復雜度為O ( V ) O(V)O(V)。
對於n個頂點e條邊的圖來說,鄰接矩陣由於是二維數組,要查找每個頂點的鄰接點需要訪問矩陣中的所有元素,因此都需要O ( V 2 ) O(V^2)O(V 2 )的時間。而鄰接表做存儲結構時,找鄰接點所需的時間取決於頂點和邊的數量,所以是O ( V + E ) O(V+E)O(V+E)。 顯然對於點多邊少的稀疏圖來說,鄰接表結構使得算法在時間效率上大大提高。
對於有向圖而言,由於它只是對通道存在可行或不可行,算法上沒有變化,是完全可以通用的。
圖的遍歷之廣度優先遍歷
廣度優先遍歷(Breadth First Search),又稱為廣度優先搜索,簡稱BFS。
基本思想:——仿樹的層次遍歷過程。
遍歷步驟:在訪問了起始點v之后,依次訪問 v的鄰接點;然后再依次(順序)訪問這些點(下一層)中未被訪問過的鄰接點;直到所有頂點都被訪問過為止。
廣度優先搜索是一種分層的搜索過程,每向前走一步可能訪問一批頂點,不像深度優先搜索那樣有回退的情況。
因此,廣度優先搜索不是一個遞歸的過程,其算法也不是遞歸的
給出代碼(鄰接矩陣):
void BFSTraverse(Mgraph *mgraph, int n)
{
int v,w,u;
int visited[n];
int Q[n+1],r,f; //Q為隊列, 其中r,f分別為隊尾指針和對頭指針
//初始化visited數組和隊列
int i;
for(i = 0;i<n;i++)
{
visited[i] = 0;
}
f = 0;
r = 0;
for(v = 0;v< mgraph->vexnum;v++)
{
if(!visited[v])
{
visited[v] = 1;//默認從v = 0 開始訪問(第一訪問)
printf("%d ",mgraph->vexs[v]);//打印
Q[r] = v;//入隊列
r = (r+1)%(n+1);
while(f<r) //隊列非空時
{
u = Q[f];f = (f+1)%(n+1);//出隊並將出隊數賦值給u
for(w = 0;w<mgraph->vexnum;w++)
{
if(mgraph->arcs[u][w]==1 && visited[w] == 0) //遍歷該頂點所有鄰接點,找該頂點是否存在沒有訪問過的鄰接點
{//找到並輸出 和入隊
visited[w] = 1;
printf("%d ",mgraph->vexs[w]);
Q[r] = w;r = (r+1)%(n+1);
}
}
}
}
}
}
給出代碼(鄰接表):
void BFSTraverse(ALGraph *g ,int n)
{
int v,w,u;
int Q[n+1];
int visited[g->vexnum];
int r = 0 ,f = 0;
ArcNode *p;
for(int i = 0;i<n+1;i++) Q[i] = 0;
for(v=0;v<g->vexnum;v++) visited[v]=0 ;//初始化visited數組
//隊列Q初始化
for(v=0;v<g->vexnum ;v++) //從v出發廣度優先搜索
{ if (!visited[v])
{
visited[v]=1;
printf("%d ",v); //輸出v
Q[r] = v; r = (r+1)%(n+1); //v入隊
while(f<r || f>r) //隊列Q不空
{
u = Q[f]; f = (f+1)%(n+1);// u出隊
p=g->adjlist[u].firstarc ;
while(p) //依次訪問u的鄰接點
{ w=p->adjvex;
if(!visited[w])
{ visited[w]=1;
printf("%d ",w);
Q[r] = w; r = (r+1)%(n+1); //w 入隊
}
p=p->nextarc ; //p指向下一個鄰接點
}//end of while
}//end of while
}//end of if
}//end of for
}//end of BFSTravers
附———圖的創建以及遍歷完整代碼(鄰接矩陣和鄰接表)
鄰接矩陣(帶注釋)
點擊查看代碼
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_VERTEX_NUM 100 /*最大頂點數設為100*/
/*---圖的鄰接矩陣表示---*/
typedef struct
{
int vexs[MAX_VERTEX_NUM]; /*頂點向量*/
int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*鄰接距陣*/
int vexnum,arcnum; /*頂點數和邊數*/
int kind; /*---圖的類型(1:有向圖 0:無向圖)---*/
}Mgraph;
bool InitMgraph(Mgraph *mgraph,int vexnum,int arcnum,int kind)
{
if(!mgraph)
{
return false;
}
/*---初始化邊的一些基本信息---*/
mgraph->vexnum = vexnum;
mgraph->arcnum = arcnum;
mgraph->kind = kind;
int i;
int j;
for(i=0;i<MAX_VERTEX_NUM;i++)
{
mgraph->vexs[i] = 0;
for(j=0;j<MAX_VERTEX_NUM;j++)
{
mgraph->arcs[i][j] = 0;
}
}
return true;
}
bool CreatMgraph(Mgraph *mgraph)
{
if(!mgraph)
{
return false;
}
int i,j;
for(i=0;i<mgraph->vexnum;i++)
{
mgraph->vexs[i] = i;
}
int count = 0;
while(count<mgraph->arcnum)
{
scanf("%d,%d",&i,&j);
if(mgraph->kind==0)
{
mgraph->arcs[i][j] = 1;
mgraph->arcs[j][i] = 1;
}
else
mgraph->arcs[i][j] = 1;
count++;
}
return true;
}
void printMgraph(Mgraph *mgraph)
{
printf("++++++++++++++++++++++++\n");
printf("+++++圖的基本信息+++++++\n");
printf("+頂點數:%d \n",mgraph->vexnum);
printf("+邊數 :%d \n",mgraph->arcnum);
printf("鄰接矩陣:\n");
int i;
int j;
for(i = 0;i<mgraph->vexnum;i++)
{
for(j = 0;j<mgraph->vexnum;j++)
{
printf(" %d",mgraph->arcs[i][j]);
}
printf("\n");
}
}
void DFS(Mgraph *m,int i,int visited[])
{
printf("%d",m->vexs[i]);
visited[i] = 1;
for(int j = 0;j<m->vexnum;j++)
{
if(m->arcs[i][j] == 1 && visited[j] == 0)
{
DFS(m,j,visited);
}
}
}
void DFST(Mgraph *m)
{
int visited[m->vexnum];
for(int i = 0;i<m->vexnum;i++)
{
visited[i] = 0;
}
for(int i = 0;i<m->vexnum;i++)
{
if(visited[i]==0)
DFS(m,i,visited);
}
}
void BFST(Mgraph *m)
{
int u,v,w;
int visited[m->vexnum];
int Q[m->vexnum+1];
int r,f;
r = 0; f = 0;
for(int i = 0;i<m->vexnum;i++)
{
visited[i] = 0;
}
for(int i = 0;i<m->vexnum;i++)
{
if(!visited[i])
{
printf("%d",m->vexs[i]);
visited[i] = 1;
Q[r] = i; r = (r+1)%(m->vexnum+1);
while(r!=f)
{
u = Q[f];f = (f+1)%(m->vexnum+1);
for(int j = 0;j<m->vexnum;j++)
{
if(m->arcs[u][j] == 1 && visited[j] == 0)
{
visited[j] = 1;
printf("%d",m->vexs[j]);
Q[r] = j; r = (r+1)%(m->vexnum+1);
}
}
}
}
}
}
int main()
{
Mgraph *m1;
m1 = (Mgraph*)malloc(sizeof(Mgraph));
int num,s,kind;
scanf("%d",&kind);
scanf("%d,%d",&num,&s);
InitMgraph(m1,num,s,kind);
CreatMgraph(m1);
DFST(m1);
BFST(m1);
return 0;
}
點擊查看代碼
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/*-------------- 圖的鄰接表--------*/
#define MAX_VERTEX_NUM 100
typedef struct ArcNode{
int adjvex; //該弧所指向的頂點位置
struct ArcNode *nextarc; //指向下一條弧的指針
int weight;
}ArcNode; //表結點 (狐)
typedef struct VNode{
int data; //頂點信息
ArcNode *firstarc; //指向第一條依附該頂點的弧的指針
}Vnode; //頂點
typedef struct {
Vnode adjlist [MAX_VERTEX_NUM]; /*鄰接表*/
int vexnum,arcnum; //頂點數,邊數
int kind;
}ALGraph; //圖的鄰接表
bool InitALGraph(ALGraph *algmraph,int vexnum,int arcnum,int kind)
{
if(!algmraph)
{
return false;
}
algmraph->vexnum = vexnum;
algmraph->arcnum = arcnum;
algmraph->kind = kind;
for(int i = 0;i<algmraph->vexnum;i++)
{
algmraph->adjlist[i].firstarc = NULL;
algmraph->adjlist[i].data = i;
}
return true;
}
bool CreatALGraph(ALGraph *algraph)
{
if(!algraph)
{
return false;
}
int count = 0;
ArcNode *p;
int i,j;
while(count<algraph->arcnum)
{
scanf("%d,%d",&i,&j);
p = (ArcNode *)malloc(sizeof(ArcNode));
p->adjvex = j;
p->nextarc = algraph->adjlist[i].firstarc;
algraph->adjlist[i].firstarc = p;
if(algraph->kind==0)
{
p = (ArcNode *)malloc(sizeof(ArcNode));
p->adjvex = i;
p->nextarc = algraph->adjlist[j].firstarc;
algraph->adjlist[j].firstarc = p;
}
count++;
}
return true;
}
void printALGraph(ALGraph *algraph)
{
printf("++++++++++++++++++++++++++\n");
printf("+邊數:%d 定點數:%d+\n",algraph->vexnum,algraph->arcnum);
printf("鄰接表:\n");
ArcNode *p;
for(int i = 0;i<algraph->vexnum;i++)
{
p = algraph->adjlist[i].firstarc;
while(p!=NULL)
{
printf("(%d,%d) ",i,p->adjvex);
p = p->nextarc;
}
printf("\n");
}
}
void DFS(ALGraph *m,int i, int visited[])
{
ArcNode *p;
printf("%d",m->adjlist[i].data);
p = m->adjlist[i].firstarc;
visited[i] = 1;
while(p)
{
if(visited[p->adjvex] == 0)
{
DFS(m,p->adjvex,visited);
}
p = p->nextarc;
}
}
void DFST(ALGraph *m)
{
int visited[m->vexnum];
for(int i = 0;i<m->vexnum;i++) visited[i] = 0;
for(int i = 0;i<m->vexnum;i++)
{
if(!visited[i])
{
DFS(m,i,visited);
}
}
}
void BFST(ALGraph *m)
{
int Q[m->vexnum+1];
int r,f;
r = 0;f = 0;
int w;
ArcNode *p;
int visited[m->vexnum];
for(int i = 0;i<m->vexnum;i++) visited[i] = 0,Q[i] = 0;
for(int i = 0;i<m->vexnum;i++)
{
if(!visited[i])
{
printf("%d",i);
Q[r] = i;r = (r+1)%(m->vexnum+1);
visited[i] = 1;
while(r!=f)
{
w = Q[f];f = (f+1)%(m->vexnum+1);
p = m->adjlist[w].firstarc;
while(p)
{
if(!visited[p->adjvex])
{
printf("%d",m->adjlist[p->adjvex].data);
Q[r] = p->adjvex;r = (r+1)%(m->vexnum+1);
visited[p->adjvex] = 1;
}
p = p->nextarc;
}
}
}
}
}
int main()
{
ALGraph *al;
al = (ALGraph*)malloc(sizeof(ALGraph));
int kind;
scanf("%d",&kind);
int i,j;
scanf("%d,%d",&i,&j);
InitALGraph(al,i,j,kind);
CreatALGraph(al);
DFST(al);
BFST(al);
printALGraph(al);
return 0;
}