圖的表示與遍歷
這個作業屬於哪個課程 | https://edu.cnblogs.com/campus/qdu/DS2020 |
---|---|
這個作業要求在哪裡 | <https://edu.cnblogs.com/campus/qdu/DS2020/homework/11472 |
這個作業的目標 | 圖的鄰接矩陣和鄰接表示、深度優先和廣度優先搜尋方法 |
學號 | 2018204091 |
一、實驗目的
1、掌握圖的鄰接矩陣和鄰接表表示
2、掌握圖的深度優先和廣度優先搜尋方法
3、理解圖的應用方法
二、實驗預習
說明以下概念
1、深度優先搜尋遍歷:對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次.
2、廣度優先搜尋遍歷:從某個頂點V出發,訪問該頂點的所有鄰接點V1,V2..VN;從鄰接點V1,V2...VN出發,再訪問他們各自的所有鄰接點;重複上述步驟,直到所有的頂點都被訪問過。
3、拓撲排序:對一個有向無環圖G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u線上性序列中出現在v之前。
4、最小生成樹:一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結點,並且有保持圖連通的最少的邊。
5、最短路徑:用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。
三、實驗內容和要求
1、閱讀並執行下面程式,根據輸入寫出執行結果。
#include<stdio.h> #define N 20 #define TRUE 1 #define FALSE 0 int visited[N]; typedef struct{ /*佇列的定義*/ int data[N]; int front,rear; }queue; typedef struct{ /*圖的鄰接矩陣*/ int vexnum,arcnum; char vexs[N]; int arcs[N][N]; }graph; void createGraph(graph *g);/*建立一個無向圖的鄰接矩陣*/ void dfs(int i,graph *g);/*從第i歌頂點出發深度優先搜尋*/ void tdfs(graph *g); /*深度優先搜尋整個圖*/ void bfs(int k,graph *g);/*從第k個頂點廣度優先搜尋整個圖*/ void tbfs(graph *g); /*廣度優先搜尋整個圖*/ void init_visit(); /*初始化訪問標識陣列*/ void createGraph(graph *g){ /*建立一個無向圖的鄰接矩陣*/ int i,j; char v; g->vexnum=0; g->arcnum=0; i=0; printf("輸出頂點序列(以#結束):\n"); while(v=getchar()!='#') { g->vexs[i]=v; /*讀入頂點資訊*/ i++; } g->vexnum=i; /*頂點數目*/ for(i=0;i<g->vexnum;i++) /*鄰接矩陣初始化*/ for(j=0;j<g->vexnum;j++) g->arcs[i][j]=0; printf("輸入的資訊:\n"); scanf("%d,%d",&i,&j); /*讀入邊i,j*/ while(i!=j) /*讀入i,j為-1時結束*/ { g->arcs[i][j]=1; g->arcs[j][i]=1; scanf("%d,%d",&i,&j); } } void dfs(int i,graph *g){ /*從第i個頂點出發深度優先搜尋*/ int j; printf("%c",g->vexs[i]); visited[i]=TRUE; for(j=0;j<g->vexnum;j++) if((g->arcs[i][j]==1)&&(!visited[j])) dfs(j,g); } void tdfs(graph *g){ /*深度優先搜尋整個圖*/ int i; printf("\n從頂點%c開始深度優先搜尋序列:",g->vexs[0]); for(i=0;i<g->vexnum;i++) if(visited[i]!=TRUE) dfs(i,g); } void bfs(int k,graph *g){ /*從第k個頂點出發廣度優先搜尋*/ int i,j; queue qlist,*q; q=&qlist; q->rear=0; q->front=0; printf("%c",g->vexs[k]); visited[k]=TRUE; q->data[q->rear]=k; q->rear=(q->rear+1)%N; while(q->rear!=q->front) { i=q->data[q->front]; q->front=(q->front+1)%N; for(j=0;j<g->vexnum;j++) if((g->arcs[i][j]==1)&&(!visited[j])) { printf("%c",g->vexs[j]); visited[j]=TRUE; q->data[q->rear]=j; q->rear=(q->rear+1)%N; } } } void tbfs(graph *g){ /*廣度優先搜尋整個圖*/ int i; printf("\n從頂點%c開始廣度優先搜尋序列:",g->vexs[0]); for(i=0;i<g->vexnum;i++) if(visited[i]!=TRUE) bfs(i,g); } void init_visit(){ /*初始化訪問標識陣列*/ int i; for(i=0;i<N;i++) visited[i]=FALSE; } int main(){ graph ga; int i,j; createGraph(&ga); printf("無向圖的鄰接矩陣:\n"); for(i=0;i<ga.vexnum;i++) { for(j=0;j<ga.vexnum;j++) printf("%3d",ga.arcs[i][j]); printf("\n"); } init_visit(); tdfs(&ga); init_visit(); tbfs(&ga); return 0; }
根據右圖的結構驗證實驗,
輸入:
ABCDEFGH#
0,1
0,2
0,5
1,3
1,4
2,5
2,6
3,7
4,7
-1,-1
·執行結果:
2、閱讀並執行下面程式,補充拓撲排序演算法。
#include<stdio.h> #include<malloc.h> #define N 20 typedef struct edgenode{ /*圖的鄰接表:鄰接連結串列結點*/ int adjvex; /*頂點序號*/ struct edgenode *next; /*下個結點的指標*/ }edgenode; typedef struct vnode{ /*圖的鄰接表:鄰接表*/ char data; /*頂點資訊*/ int ind; /*頂點入度*/ struct edgenode *link; /*指向鄰接連結串列指標*/ }vnode; void createGraph_list(vnode adjlist[],int *p);/*建立有向圖的鄰接表*/ void topSort(vnode g[],int n);/*拓撲排序*/ void createGraph_list(vnode adjlist[],int *p){ int i,j,n,e; char v; edgenode *s; i=0;n=0;e=0; printf("輸入頂點序列(以#結束):\n"); while((v=getchar())!='#') { adjlist[i].data=v; /*讀入頂點資訊*/ adjlist[i].link=NULL; adjlist[i].ind=0; i++; } n=i; *p=n; /*建立鄰接連結串列*/ printf("\n請輸入弧的資訊(i=-1結束):i,j:\n"); scanf("%d,%d",&i,&j); while(i!=-1) { s=(struct edgenode*)malloc(sizeof(edgenode)); s->adjvex=j; s->next=adjlist[i].link; adjlist[i].link=s; adjlist[j].ind++;/*頂點j的入度加1*/ e++; scanf("%d,%d",&i,&j); } printf("鄰接表:"); for(i=0;i<n;i++) /*輸出鄰接表*/ { printf("\n%c,%d",adjlist[i].data,adjlist[i].ind); s=adjlist[i].link; while(s!=NULL) { printf("->%d",s->adjvex); s=s->next; } } } void topSort(vnode g[],int n){ printf("輸入拓撲排序頂點序列:\n"); int i,j,k,m=0,top=-1; struct edgenode *p; for (i=0; i<=n; i++) //將度為零的頂點入棧 if (g[i].ind==0) { g[i].ind=top; top=i; } while (top!=-1) //棧不為空 { j=top; top=g[top].ind; //出棧 printf("%c",g[j].data); m++; p=g[j].link; while (p) //刪除該節點的所有邊 { k=p->adjvex; g[k].ind--; if (g[k].ind==0) //將入度為零的點入棧 { g[k].ind=top; top=k; } p=p->next; } } if (m<n) printf("該圖存在環\n"); } int main(){ vnode adjlist[N]; int n,*p; p=&n; createGraph_list(adjlist,p); return 0; }
根據輸入,輸出有向圖的拓撲排序序列。並畫出有向圖。輸入:
ABCDEF#
0,1
1,2
2,3
4,1
4,5
-1,-1
· 執行結果:
3、閱讀並執行下面程式。
#include<stdio.h>
#define N 20
#define TRUE 1
#define INF 32766 /*鄰接矩陣中的無窮大元素*/
#define INFIN 32767 /*比無窮大元素大的數*/
typedef struct{ /*圖的鄰接矩陣*/
int vexnum,arcnum;
char vexs[N];
int arcs[N][N];
}
graph;
void createGraph_w(graph *g,int flag);
void prim(graph *g,int u);
void dijkstra(graph g,int v);
void showprim();
void showdij();
/*建帶權圖的鄰接矩陣,若flag為1則為無向圖,flag為0為有向圖*/
void createGraph_w(graph *g,int flag){
int i,j,w;
char v;
g->vexnum=0;
g->arcnum=0;
i=0;
printf("輸入頂點序列(以#結束):\n");
while((v=getchar())!='#')
{
g->vexs[i]=v; /*讀入頂點資訊*/
i++;
}
g->vexnum=i;
for(i=0;i<6;i++) /*鄰接矩陣初始化*/
for(j=0;j<6;j++)
g->arcs[i][j]=INF;
printf("輸入邊的資訊:\n");
scanf("%d,%d,%d",&i,&j,&w); /*讀入邊(i,j,w)*/
while(i!=-1) /*讀入i為-1時結束*/
{
g->arcs[i][j]=w;
if(flag==1)
g->arcs[j][i]=w;
scanf("%d,%d,%d",&i,&j,&w);
}
}
void prim(graph *g,int u)/*出發頂點u*/
{
int lowcost[N],closest[N],i,j,k,min;
for(i=0;i<g->vexnum;i++) /*求其他頂點到出發頂點u的權*/
{
lowcost[i]=g->arcs[u][i];
closest[i]=u;
}
lowcost[u]=0;
for(i=1;i<g->vexnum;i++) /*迴圈求最小生成樹中的各條邊*/
{ min=INFIN;
for(j=0;j<g->vexnum;j++) /*選擇得到一條代價最小的邊*/
if(lowcost[j]!=0&&lowcost[j]<min)
{
min=lowcost[j];
k=j;
}
printf("(%c,%c)--%d\n",g->vexs[closest[k]],g->vexs[k],lowcost[k]); /*輸出該邊*/
lowcost[k]=0; /*頂點k納入最小生成樹 */
for(j=0;j<g->vexnum;j++) /*求其他頂點到頂點k 的權*/
if(g->arcs[k][j]!=0&&g->arcs[k][j]<lowcost[j])
{
lowcost[j]=g->arcs[k][j];
closest[j]=k;
}
}
}
void printPath(graph g,int startVex,int EndVex)
{
int path[N][N],stack[N],top=0; /*堆疊*/
int i,k,j;
int flag[N]; /*輸出路徑頂點標誌*/
k=EndVex;
for (i=0;i<g.vexnum;i++) flag[i]=0;
j=startVex;
printf("%c",g.vexs[j]);
flag[j]=1;
stack[top++]=k;
while (top>0) /*找j到k的路徑*/
{
for (i=0;i<g.vexnum;i++)
{
if (path[k][i]==1 && flag[i]==0) /*j到k的路徑含有i頂點*/
{
if (g.arcs[j][i]!=INF ) /*j到i的路徑含有中間頂點*/
{
printf("-> %c(%d) ",g.vexs[i],g.arcs[j][i]);
/*輸出j到k的路徑的頂點i*/
flag[i]=1;
j=i;
k=stack[--top];
break;
}
else
{
if (i!=k) stack[top++]=i; /*break;*/
}
}
}
}
}
void dijkstra(graph g,int v){ /*dijkstra演算法求單源最短路徑*/
int path[N][N],dist[N],s[N];
int mindis,i,j,u,k;
for(i=0;i<g.vexnum;i++){
dist[i]=g.arcs[v][i];
s[i]=0;
for(j=0;j<g.vexnum;j++)
path[i][j]=0;
if(dist[i]<INF){
path[i][v]=1;
path[i][i]=1;
}
}
dist[v]=0;
s[v]=1;
for(i=0,u=1;i<g.vexnum;i++){
mindis=INFIN;
for(j=0;j<g.vexnum;j++)
if(s[j]==0)
if(dist[j]<mindis){
u=j;
mindis=dist[j];
}
s[u]=1;
for(j=0;j<g.vexnum;j++)
if((s[j]==0)&&dist[u]+g.arcs[u][j]<dist[j]){
dist[j]=dist[u]+g.arcs[u][j];
for(k=0;k<g.vexnum;k++)
path[j][k]=path[u][k];
path[j][j]=1;
}
}
printf("\n頂點%c->到各頂點的最短路徑\n",g.vexs[v]);
for(i=0;i<g.vexnum;i++){
printf("\n頂點%c->頂點%c:",g.vexs[v],g.vexs[i]);
if(dist[i]==INF||dist[i]==0)
printf("無路徑");
else{
printf("%d ",dist[i]);
printf("經過頂點:");
printPath(g,v,i); /*輸出v到i的路徑*/
}
}
}
void showprim()/*最小生成樹prim演算法演示*/
{
graph ga;
createGraph_w(&ga,1);
prim(&ga,0);
}
void showdij(){ /*dijstra演算法演示*/
graph ga;
createGraph_w(&ga,0);
dijkstra(ga,0);
}
int main(){
showprim(); /*prim演算法演示*/
getchar();
showdij(); /*dijstra演算法演示*/
return 0;
}
下面的輸入分別驗證prim演算法和dijstra演算法。輸入例項的第一部分為無向圖,求其最小生成樹;輸入的第二部分為有向圖,求其最短路徑。
ABCDEF#
0,1,6
0,2,1
0,3,5
1,2,5
1,4,3
2,3,5
2,4,6
2,5,4
3,5,2
4,5,6
-1,-1,-1
ABCDEF#
0,2,10
0,5,100
0,4,30
1,2,5
2,3,50
3,4,20
3,5,10
4,3,20
4,5,60
-1,-1,-1
·執行結果:(並畫出兩個圖)
四、實驗小結
學習了本節中各個概念,演算法表示,以及利用演算法來求最小生成樹以及最短路徑。