1. 程式人生 > 實用技巧 >圖的表示與遍歷

圖的表示與遍歷

這個作業屬於哪個課程 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

·執行結果:(並畫出兩個圖)

四、實驗小結
學習了本節中各個概念,演算法表示,以及利用演算法來求最小生成樹以及最短路徑。