Bootstrap

数据结构实验十二 图的遍历及应用

数据结构实验十二 图的遍历及应用

一、【实验目的】

1、 理解图的存储结构与基本操作;

2、熟悉图的深度度优先遍历和广度优先遍历算法

3、掌握图的单源最短路径算法

二、【实验内容】

1.根据下图(图见实验11)邻接矩阵,编程实现该图的深度与广度优先遍历算法,输出遍历序列。

2.单源节点最短路径问题

问题描述:求从有向图的某一结点出发到其余各结点的最短路径。

基本要求:

(1)有向图采用邻接矩阵表示。

(2)单源节点最短路径问题采用Dijkstra算法。

(3)输出有向图中从源结点T到其余各结点的最短路径和最短路径值。

三、实验源代码

//SequenceList.h

typedef struct
{
	ElemType list[MaxSize];
    int size;        
} SequenceList;

//顺序表初始化 
void ListInitialize(SequenceList *L)
{
    L->size = 0;
}

int ListLength(SequenceList L)
{
    return L.size;
}

//顺序表插入操作 
int ListInsert(SequenceList *L, int i, ElemType x)
{
    int j;
    if (L->size >= MaxSize)
    {
        printf("顺序表已满无法插入!\n");
        return 0;
    }
    else if (i < 0 || i > L->size)
    {
        printf("参数i不合法!\n");
        return 0;
    }
    else
    {
        for (j = L->size; j > i; j--)
        {
            L->list[j] = L->list[j-1];
        }
        L->list[i] = x;
        L->size++;
    }
    return 1;
}

int ListDelete(SequenceList *L, int i, ElemType *x)
{
    int j;
    if (L->size <= 0)
    {
        printf("顺序表已空无数据可删!\n");
        return 0;
    }
    else if (i < 0 || i > L->size-1)
    {
        printf("参数i不合法");
        return 0;
    }
    else
    {
        *x = L->list[i];
        for (j = i+1; j < L->size-1; j++) 
        {
            L->list[j-1] = L->list[j];
        }
        L->size--;
        return 1;
    }
}

int ListGet(SequenceList L, int i, ElemType *x)
{
    if (i < 0 || i > L.size-1)
    {
        printf("参数i不合法!\n");
        return 0;
    }
    else
    {
        *x = L.list[i];
        return 1;
    }
}

SequenceQueue.h
//定义循环队列结构体
typedef struct
{
	ElemType queue[MaxQueueSize];
	int rear;
	int front;
	int count;
 } SequenceQueue;

//初始化顺序循环队列 
void QueueInitiate(SequenceQueue *Q)
{
	Q->rear =0;
	Q->front=0;
	Q->count=0; 
}

//判空 
int QueueNotEmpty(SequenceQueue Q)
{
	if(Q.count ==0)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

//入队 
int QueueAppend(SequenceQueue *Q,ElemType x)
{
	if(Q->count>0&&Q->front==(Q->front+Q->count)%40)
	{
		printf("队列已满无法插入!\n");
		return 0;
	}
	else
	{
		Q->queue[Q->rear] = x;
		Q->rear=(Q->rear+1)%MaxQueueSize;
		Q->count ++;
		
		return 1;
	}
}

//出队 
int QueueDelete(SequenceQueue *Q,int *d)
{
	if(Q->count==0)
	{
		printf("循环队列已空无数据元素出队列!\n");
		return 0;
	}
	else
	{
		*d=Q->queue[Q->front];
		Q->front=(Q->front+1)%MaxQueueSize;
		Q->count--;
		return 1;
	}
}
//12.c
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 100
#define MaxVertices 10
#define MaxEdges 100
#define MaxWeight 10000

#define MaxQueueSize 10

typedef char ElemType;

#include"SequenceList.h" 

typedef struct
{
	SequenceList Vertices;
	int edge[MaxVertices][MaxVertices];
	int numOfEdges;
}MatrixGraph;



typedef struct 
{
	int row;
	int col;
	int weight;
}RCW;

//初始化 
void Initiate(MatrixGraph *G,int n)
{
	int i,j;
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			if(i==j)
			{
				G->edge[i][j]=0;
			}
			else
			{
				G->edge[i][j]=MaxWeight;
			}
		}
	}
	G->numOfEdges=0;//边的条数置为0 
	ListInitialize(&G->Vertices);//顺序表初始化 
}

//插入顶点
void InsertVertex(MatrixGraph *G,ElemType vertex)
{
	ListInsert(&G->Vertices,G->Vertices.size,vertex);// 
 } 
 
 //插入边 
 void InsertEdge(MatrixGraph *G,int v1,int v2,int weight)
 {
 	if(v1<0||v1>G->Vertices.size||v2<0||v2>G->Vertices.size)
	{
	 	printf("参数v1或参数v2越界出错!\n");
		return; 
	} 
	G->edge[v1][v2]=weight;
	G->numOfEdges++;
 }

void CreatGraph(MatrixGraph *G,ElemType V[],int n,RCW E[],int e)
{
	int i,k;
	Initiate(G,n);
	for(i=0;i<n;i++)
	{
		InsertVertex(G,V[i]);
	}
	for(k=0;k<e;k++)
	{
		InsertEdge(G,E[k].row,E[k].col,E[k].weight);
	 } 
}

//取第一个邻接顶点
int GetFirstVex(MatrixGraph G,int v,int visited[])
{
	int col;
	if(v<0||v>G.Vertices.size)
	{
		printf("参数v1越界出错!\n");
		return -1;
	}
	for(col=0;col<=G.Vertices.size;col++)
	{
		if(!visited[col])
			if(G.edge[v][col]>0&&G.edge[v][col]<MaxWeight)
			{

				return col;
			}
	}
	return -1;
 } 
 
 //取下一个邻接顶点
 int GetNextVex(MatrixGraph G,int v1,int v2,int visited[])
 {
 	int col;

 	if(v1<0||v1>G.Vertices.size||v2<0||v2>G.Vertices.size)
 	{
 		printf("参数v1或v2越界出错!\n");
 		return -1;
	 }
	 for(col=v2+1;col<=G.Vertices.size;col++)
	 {
		if(!visited[col])
		{
			if(G.edge[v1][col]>0&&G.edge[v1][col]<MaxWeight)
	 		{
	 		return col;
		 	}
		 	
		}
	 }
	 return -1;
  } 

//深度优先遍历
void DepthFSearch(MatrixGraph G,int v,int visited[])
{
	int w;
	printf("%c ",G.Vertices.list[v]);
	visited[v]=1;
	w=GetFirstVex(G,v,visited);
	while(w!=-1) 
	{
		if(!visited[w])
			DepthFSearch(G,w,visited);
		w=GetNextVex(G,v,w,visited);
	}
 } 
 
 //引用顺序队列头文件 
 #include"SequenceQueue.h"
 
 //取第一个邻接顶点
int GetFirstVex2(MatrixGraph G,int v,int visited[])
{
	int col;
	if(v<0||v>G.Vertices.size)
	{
		printf("参数v1越界出错!\n");
		return -1;
	}
	for(col=0;col<=G.Vertices.size;col++)
	{
			if(G.edge[v][col]>0&&G.edge[v][col]<MaxWeight)
			{

				return col;
			}
	}
	return -1;
 } 
 
 //广度优先遍历
 void BroadFSearch(MatrixGraph G,int v,int visited2[])
 {
 	int u,w;
 	SequenceQueue queue;
 	printf("%c ",G.Vertices.list[v]);
 	visited2[v]=1;
 	QueueInitiate(&queue);
 	QueueAppend(&queue,v);
 	while(QueueNotEmpty(queue))
 	{
 		QueueDelete(&queue,&u);//传地址? 
 
 		w=GetFirstVex2(G,u,visited2);
 		while(w!=-1)
 		{
 			if(!visited2[w])
 			{
 				printf("%c ",G.Vertices.list[w]);
 				visited2[w]=1;
 				QueueAppend(&queue,w);
			 }
			 w=GetNextVex(G,u,w,visited2);
		 }
	 }
  } 
  
//狄克斯特拉算法找最短路径
//带权图G从下标v0顶点到其他顶点的最短距离distance
//和最短路径下标path 
void Dijkstra(MatrixGraph G,int v0,int distance[],int path[]) 
{ 
	int n=G.Vertices.size;
	int *s=(int*)malloc(sizeof(int)*n);
	int minDis,i,j,u;
	for(i=0;i<n;i++)//初始化
	{
		distance[i]=G.edge[v0][i];
		s[i]=0;
		if(i!=v0&&distance[i]<MaxWeight)
			path[i]=v0;
		else
			path[i]=-1;
	 } 
	 s[v0]=1;
	 for(i=1;i<n;i++)
	 {
	 	minDis=MaxWeight;
	 	for(j=0;j<=n;j++)//在还未找到最短路径的顶点集中选有最短距离的顶点u 
	 	{
	 		if(s[j]==0&&distance[j]<minDis)
	 		{
	 			u=j;
	 			minDis=distance[j];
			 }
		 }
		if(minDis==MaxWeight)//当已不存在路径时算法结束 
			return;
		s[u]=1;//标记顶点u已从集合T加入到集合S中 
		for(j=0;j<n;j++)//修改从v0到其他顶点的最短距离和最短路径 
		{
			if(s[j]==0&&G.edge[u][j]<MaxWeight&&distance[u]+G.edge[u][j]<distance[j])
			{
				distance[j]=distance[u]+G.edge[u][j];
				path[j]=u;
			 } 
		 } 
	 }
}

//正序输出最短路径
printfpath(MatrixGraph g1,int path[])
{
	char path2[6];
	int i,j,k;
	for(i=1;i<6;i++)
	{
		printf("\n");
		j=i;
		k=0;
		while(j!=-1)
		{
			path2[k]=g1.Vertices.list[j];
			k++;
			j=path[j];	
		}
		k--;
		printf("%c",path2[k]);
		while(k>0)
		{
			k--;
			printf("->%c",path2[k]);
		}
	 } 
 } 

void main(void)
{
	MatrixGraph g1;
	ElemType a[]={'A','B','C','D','E','F'};
	RCW rcw[]={{0,2,5},{0,3,30},{1,0,2},{1,4,8},{2,1,15},{2,5,7},{4,3,4},{5,3,10},{5,4,18}};
	int n=6,e=9;
	int i,j;
	CreatGraph(&g1,a,n,rcw,e);
	printf("顶点集合为:");
	for(i=0;i<g1.Vertices.size;i++)
	{
		printf("%c  ",g1.Vertices.list[i]);
	 } 
	 printf("\n");
	 printf("邻接矩阵为:\n");
	 for(i=0;i<g1.Vertices.size;i++)
	 {
	 	for(j=0;j<g1.Vertices.size;j++)
	 	{
	 		printf("%5d  ",g1.edge[i][j]);
		}
		printf("\n");
	 }
	printf("深度优先遍历结果为:"); 
	int visited[6]={};
	DepthFSearch(g1,0,visited);
	printf("\n");
	int visited2[6]={};
	printf("广度优先遍历结果为:");
	BroadFSearch(g1,0,visited2); 
	printf("\n");
	printf("源结点T到其余各结点的最短路径为:");
	int distance[6];
	int path[6]={-1};
	Dijkstra(g1,0,distance,path);
	printfpath(g1,path);
	printf("\n");
	printf("源结点T到其余各结点的最短路径值为:");
	for(i=0;i<n;i++)
	{
		printf("\n%c->%c:",g1.Vertices.list[0],g1.Vertices.list[i]);
		printf("%d",distance[i]);
	}
}

四、实验结果
在这里插入图片描述

五、实验总结
总结狄克斯特拉算法:
首先,使用 malloc 分配了一个大小为 n 的整型数组 s,用于标记顶点是否已经加入集合 S 中。
然后进行了初始化,初始化了距离数组 distance 和标记数组 s,以及路径数组 path。初始化时,将源顶点 v0 到其他顶点的距离和路径进行了设置。
接下来使用一个循环来依次将未加入集合 S 中的顶点加入,并更新最短路径和最短距离。在每次循环中,首先找到未加入集合 S 中且距离最短的顶点 u,然后将其标记为已加入集合 S 中。
在找到顶点 u 后,再次遍历所有未加入集合 S 中的顶点,更新从源顶点 v0 到其他顶点的最短距离和最短路径。如果通过顶点 u 可以找到更短的路径,就更新距离数组 distance 和路径数组 path。
最终得到的 distance 数组中存储了从源顶点 v0 到各顶点的最短距离,path 数组中存储了相应的最短路径。

;