图的各类算法实现
实验内容:
设图结点的元素类型为char,建立一个不少于8个顶点的带权无向图G,实现以下图的各种基本操作的程序:
① 用邻接矩阵作为储结构存储图G并输出该邻接矩阵;
② 按DFS算法输出图G中顶点的遍历序列;
③ 按BFS算法输出图G中顶点的遍历序列;
④ 按Prime算法从某个指定的顶点出发输出图G的最小生成树
输入样例:
a b c d e f g h i j k l //顶点数据
0 1 10 //边的数据 (v1 v2 权值)
0 2 4
0 3 30
1 4 11
1 5 20
2 5 5
2 6 60
3 7 55
4 8 8
4 9 100
6 9 34
6 11 19
8 9 9
9 10 8
测试数据
input1.txt
a b c d e f g h i j k l
0 1 10
0 2 4
0 3 30
1 4 11
1 5 20
2 5 5
2 6 60
3 7 55
4 8 8
4 9 100
6 9 34
6 11 19
8 9 9
9 10 8
output1.txt
a b c d e f g h i j k l
0 10 4 30 1000 1000 1000 1000 1000 1000 1000 1000
10 0 1000 1000 11 20 1000 1000 1000 1000 1000 1000
4 1000 0 1000 1000 5 60 1000 1000 1000 1000 1000
30 1000 1000 0 1000 1000 1000 55 1000 1000 1000 1000
1000 11 1000 1000 0 1000 1000 1000 8 100 1000 1000
1000 20 5 1000 1000 0 1000 1000 1000 1000 1000 1000
1000 1000 60 1000 1000 1000 0 1000 1000 34 1000 19
1000 1000 1000 55 1000 1000 1000 0 1000 1000 1000 1000
1000 1000 1000 1000 8 1000 1000 1000 0 9 1000 1000
1000 1000 1000 1000 100 1000 34 1000 9 0 8 1000
1000 1000 1000 1000 1000 1000 1000 1000 1000 8 0 1000
1000 1000 1000 1000 1000 1000 19 1000 1000 1000 1000 0
a b e i j g c f l k d h
a b c d e f g h i j l k
a c 4
c f 5
a b 10
b e 11
e i 8
i j 9
j k 8
a d 30
g j 34
g l 19
d h 55
代码体
AdjMGraph.h
/*
设图结点的元素类型为char,建立一个不少于8个顶点的带权无向图G,实现以下图的各种基本操作的程序:
① 用邻接矩阵作为储结构存储图G并输出该邻接矩阵;
② 按DFS算法输出图G中顶点的遍历序列;
③ 按BFS算法输出图G中顶点的遍历序列;
④ 按Prime算法从某个指定的顶点出发输出图G的最小生成树;
*/
#ifndef ADJMGRAPH_H
#define ADJMGRAPH_H
#include "SeqList.h"//顺序表头文件
#include "SeqCQueue.h"//顺序循环队列
#include "SeqList.h" //顺序堆栈
#define MaxVertices 50
#define MaxWeight 1000
typedef char VDataType;
typedef struct{
SeqList Vertices;//存放顶点的顺序表
int edge[MaxVertices][MaxVertices];//存放边的邻接矩阵
int numOfEdges;//边的个数
}AdjMGraph;//图的结构体定义
//最小生成树的存储结构体
typedef struct{
int v1;
int v2;
int weight;
}MinSpanTreeEage;
void AdjInitiate(AdjMGraph*G,int n);
int InsertVertex(AdjMGraph*G,VDataType vertex);
int DeleteEdge(AdjMGraph*G,int v1,int v2);
int InsertEdge(AdjMGraph*G,int v1,int v2,int weight);
int GetFirstVex(AdjMGraph G,int c);
int GetNextVex(AdjMGraph G,int v1,int v2);
void CreateGraph(AdjMGraph* G, FILE* file );
void PrintGraph(AdjMGraph G );
void DepthFSearch(AdjMGraph G,int v ,int visited[],VDataType* result,int* count );
void BroadFSearch(AdjMGraph G,int v,int visited[],VDataType* result,int* count );
void Prime(AdjMGraph G,MinSpanTreeEage closeVertex[]);
#endif
SeqCQueue.h
#ifndef SEQCQUEUE_H
#define SEQCQUEUE_H
#include <stdio.h>
#include <stdlib.h>
#define MaxQueueSize 100
typedef int QDataType;
//顺序循环队列的结构体
typedef struct Queue{
QDataType queue[MaxQueueSize];
int rear; //队尾指针
int front; //队头指针
int count; //计数器
} SeqCQueue;
void QueueInitiate(SeqCQueue *Q);
int QueueNotEmpty(SeqCQueue Q);
int QueueAppend(SeqCQueue *Q, QDataType x);
int QueueDelete(SeqCQueue *Q, QDataType *d);
int QueueGet(SeqCQueue Q, QDataType *d);
#endif
SeqList.h
#ifndef SEQLIST_H
#define SEQLIST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxSize 100
typedef char ItemType;
typedef struct List{
ItemType list[MaxSize];
int length;
} SeqList;
void ListInitiate(SeqList *L);
int ListInsert(SeqList *L, int i, ItemType x);
int ListDelete(SeqList *L, int i, ItemType *x);
int ListGet(SeqList L, int i, ItemType *x);
#endif
SeqStack.h
#ifndef SEQSTACK_H
#define SEQSTACK_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxStackSize 100
typedef int DataType;
typedef struct Stack
{
DataType stack[MaxStackSize];
int top;
} SeqStack;
void StackInitiate(SeqStack *S);
int StackNotEmpty(SeqStack S);
int StackPush(SeqStack *S, DataType x);
int StackPop(SeqStack *S, DataType *d);
int StackTop(SeqStack S, DataType *d);
#endif
SeqCQueue.c
#include "SeqCQueue.h"
//初始化QueueInitiate(Q)
void QueueInitiate(SeqCQueue *Q)
{
Q->rear = 0;
Q->front = 0;
Q->count = 0;
}
//判断循环队列Q非空否,非空则返回1,否则返回0
int QueueNotEmpty(SeqCQueue Q)
{
if(Q.count != 0) return 1;
else return 0;
}
//把数据元素值x插入顺序循环队列Q的队尾,成功返回0,失败返回-1
int QueueAppend(SeqCQueue *Q, QDataType x)
{
if(Q->count > 0 && Q->rear == Q->front)
{
printf("The Queue is full! \n");
return -1;
}
else
{
Q->queue[Q->rear] = x;
Q->rear = (Q->rear + 1) % MaxQueueSize;
Q->count++;
return 0;
}
}
//删除顺序循环队列Q的队头元素并赋值给d,成功则返回0,失败返回-1
int QueueDelete(SeqCQueue *Q, QDataType *d)
{
if(Q->count == 0){
printf("The Queue is empty! \n");
return -1;
}
else
{ *d = Q->queue[Q->front];
Q->front = (Q->front + 1) % MaxQueueSize;
Q->count--;
return 0;
}
}
//取队头数据元素 QueueGet(Q, d)
int QueueGet(SeqCQueue Q, QDataType *d)
{
if(Q.count == 0)
{
printf("The Queue is empty!\n");
return -1;
}
else
{
*d = Q.queue[Q.front];
return 0;
}
}
SeqList.c
#include "SeqList.h"
//初始化
void ListInitiate(SeqList *L)
{
L->length= 0; /*定义初始数据元素个数*/
}
//求当前数据元素个数
int ListLength(SeqList L)
{
return L.length;
}
//在顺序表L的第i(0≤i ≤ length-1)个位置插入数据元素x
//插入成功返回0,失败返回-1
int ListInsert(SeqList *L, int i, ItemType x)
{ int j;
if(L->length >MaxSize)
{
printf("The list is full!");
return -1;
}
else if(i<0||i>L->length)
{
printf("The parameter i is illegal!\n");
return -1;
} else
{
for(j = L->length; j > i; j--)
L->list[j] = L->list[j-1]; /*依次后移*/
L->list[i] = x;
L->length ++;
return 0;
}
}
//删除顺序表L中位置i(0≤i ≤ length-1)上的数据元素并保存到x中
//删除成功返回0,删除失败返回-1
int ListDelete(SeqList *L, int i, ItemType *x)
{ int j;
if(L->length <=0)
{
printf("The list is empty!");
return -1;
}
else if(i<0||i>L->length-1)
{
printf("The parameter i is illegal!");
return -1;
} else
{
*x = L->list[i]; /*保存删除的元素到x中*/
for(j=i+1; j<=L->length-1; j++)
L->list[j-1] = L->list[j]; /*依次前移*/
L->length--; /*数据元素个数减1*/
return 0;
}
}
//取数据元素,成功返回0,失败返回-1
int ListGet(SeqList L, int i, ItemType *x)
{ if(i < 0 || i > L.length-1)
{
printf("The parameter i is illegal!");
return -1;
}
else
{
*x = L.list[i];
return 0;
}
}
SeqStack.c
#include "SeqStack.h"
//初始化
void StackInitiate(SeqStack *S)
{
S->top = -1;
}
//非空否
int StackNotEmpty(SeqStack S)
{
if(S.top < 0) return 0;
else return 1;
}
//把数据元素值x存入顺序堆栈S中,入栈成功则返回0,失败返回-1
int StackPush(SeqStack *S, DataType x)
{
if(S->top > MaxStackSize-1)
{
printf("Stack is full!");
return -1;
}
else
{
S->top ++;
S->stack[S->top] = x;
return 0;
}
}
//出栈 ,成功则返回0,失败返回-1
int StackPop(SeqStack *S, DataType *d)
{
if(S->top < 0)
{
printf("The stack is empty!");
return -1;
}
else
{
*d = S->stack[S->top];
S->top --;
return 0;
}
}
//取栈顶数据元素
int StackTop(SeqStack S, DataType *d)
{
if(S.top <0)
{
printf("The stack is empty!");
return -1;
}
else
{ *d = S.stack[S.top];
return 0;
}
}
AdjMGraph.c
#include "AdjMGraph.h"
//初始化最多可以存放n个顶点的顶点顺序表和邻接矩阵
void AdjInitiate(AdjMGraph*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;
ListInitiate(&G->Vertices);
}
//在图中插入顶点vertex,顶点序号自动分配,成功返回0,失败返回-1
int InsertVertex(AdjMGraph*G, VDataType vertex){
return ListInsert(&(G->Vertices),G->Vertices.length,vertex);
}
//在图中插入边<v1,v2>,v1,v2为顶点序号,权值为weight,成功返回0, 失败返回-1
int InsertEdge(AdjMGraph*G,int v1,int v2,int weight){
if(v1<0||v1>=G->Vertices.length||v2<0||v2>=G->Vertices.length){
printf("parameter v1 or parameter v2 is illegal!",v1,v2);
return -1;
}
G->edge[v1][v2]=weight;//无向边;邻接矩阵为对称矩阵
G->edge[v2][v1]=weight;
G->numOfEdges++;
return 0;
}
//删除边<v1,v2>,v1,v2为顶点序号,成功返回0, 失败返回-1
int DeleteEdge(AdjMGraph*G,int v1,int v2){
if(v1<0||v1>=G->Vertices.length||v2<0||v2>=G->Vertices.length||v1==v2){
printf("parameter v1 or parameter v2 is illegal!",v1,v2);
return -1;
}
if(G->edge[v1][v2]==MaxWeight||v1==v2){
printf("The edge is not exist!");
return -1 ;
}
G->edge[v1][v2]=MaxWeight;
G->edge[v2][v1]=MaxWeight;
G->numOfEdges--;
return 0;
}
//取第一个邻接顶点序号,没有则返回-1
int GetFirstVex(AdjMGraph G,int v){
int col;
if(v<0||v>=G.Vertices.length){
printf("parameter v is illegal!");
return -1;
}
for(col=0;col<G.Vertices.length;col++){
if(G.edge[v][col]>0&&G.edge[v][col]<MaxWeight)
return col;
}
return -1;
}
//取下一个邻接顶点序号,没有则返回-1
int GetNextVex(AdjMGraph G,int v1,int v2){
int col;
if(v1<0||v1>=G.Vertices.length||v2<0||v2>=G.Vertices.length){
printf("parameter v1 or parameter v2 is illegal!");
return -1;
}
for(col=v2+1;col<G.Vertices.length;col++){
if(G.edge[v1][col]>0&&G.edge[v1][col]<MaxWeight)
return col;
}
return -1;
}
//从文件中读取数据,构建图
void CreateGraph(AdjMGraph* G, FILE* file ){
int i, k;
int n=0;
char c;
int v1,v2,weight;
char vdata[100];
fscanf(file,"%c",&c);
while(c!='\n'){
if(c>='a'&&c<='z'){
vdata[n]=c;
n++;
}
fscanf(file,"%c",&c);
}
AdjInitiate(G,n);//初始化顺序表和邻接矩阵
for(i=0;i<n;i++)
InsertVertex(G,vdata[i]);//插入顶点
int numberOfEges=0;
while(fscanf(file,"%d %d %d",&v1,&v2,&weight)!=EOF){
InsertEdge(G,v1,v2,weight);//插入边
numberOfEges++;
}
G->numOfEdges=numberOfEges;
}
//打印图的信息
void PrintGraph(AdjMGraph G ){
int n= G.Vertices.length;
int i,j;
for( i=0; i<n;i++){
for( j=0;j<n;j++)
printf("%-4d ",G.edge[i][j]);
printf("\n");
}
}
//深度优先遍历
//连通图G以v为初始顶点的深度优先遍历,遍历结果放入 result[],count为resualt数组中元素个数
//数组visitted标记相应顶点是否已被访问过
void DPS(AdjMGraph G,int visit[],int a,VDataType* result,int* count){
int j;
result[(*count)]=G.Vertices.list[a];
*count=(*count)+1;
visit[a]=1;
for(j=0;j<G.Vertices.length;j++) //依次搜索vi邻接点
if(G.edge[a][j]!=1000 && visit[j]==0&& a!=j)
DPS(G, visit,j,result,count);
}
void DepthFSearch(AdjMGraph G,int v ,int visited[],VDataType* result,int* count )
{
for (v = 0; v < G.Vertices.length; v++)
{
if (visited[v]==0){
DPS(G, visited,v,result,count);
}
}
}
//广度优先遍历
//连通图G以v为初始顶点的广度优先遍历,遍历结果放入 result[],count为resualt数组中元素个数
//数组visited标记相应顶点是否已被访问过
void BFS(AdjMGraph G,int visit[],int k,VDataType* result,int* count)
{
int i,j;
SeqCQueue Q;
QueueInitiate(&Q);
result[(*count)]=G.Vertices.list[k];
*count=(*count)+1;
visit[k]=1;
QueueAppend(&Q,k);
while (QueueNotEmpty(Q))
{
QueueDelete(&Q,&i);
for (j=0;j<G.Vertices.length;j++)
if(G.edge[i][j]!=1000 && visit[j]==0&& i!=j)
{
result[(*count)]=G.Vertices.list[j];
*count=(*count)+1;
visit[j]=1;
QueueAppend(&Q,j);
}
}
}
void BroadFSearch(AdjMGraph G,int v,int visited[],VDataType result[],int* count ){
for (v = 0; v < G.Vertices.length; v++)
{
visited[v]=0;
}
for (v = 0; v < G.Vertices.length; v++)
{
if (visited[v]==0){
BFS(G, visited,v,result,count);
}
}
}
//使用普里姆算法,从序号为0的点开始求最小生成树
//求得的最小生成树存入 MinSpanTreeEage closeVertex[] 数组中,数组中每个元素表示最小生成树的一条边,MinSpanTreeEage 结构体定义如下
//这里的边是从下标为1开始存放,也就是说closeVertex[1]是第一条边,而closeVertex[0]没有任何作用,main函数打印所有边的时候从closeVertex[1]到closeVertex[n-1]
/*
typedef struct{
int v1; //最小生成树的边的起始点序号 (边中序号小的为起始点)
int v2; //最小生成树的边的终点序号 (边中序号大的为终点)
int weight; //边的权值
}MinSpanTreeEage; //表示最小生成树的一条边
*/
void Prime(AdjMGraph G,MinSpanTreeEage closeVertex[])
{
int t=0;
int min, i, j ,k;
int adjvex[MaxVertices]; //保存相关顶点下标
int lowcost[MaxVertices]; //保存相关顶点间的权值
lowcost[0]=0; //初始化第一个权值为0,cost为0,即下标为0的顶点,v_{0}加入生成树
adjvex[0] = 0; //初始化第一个顶点 下标为0
//将第一行的边,存储到lowcost
for (i=1;i<G.Vertices.length;i++) //循环除了下标为0外的全部顶点
{
lowcost[i] = G.edge[0][i]; //将v_{0}顶点与之有边的权值存入数组
adjvex[i]=0; //初始化都为v_{0}的下标
}
for (i=1;i<G.Vertices.length;i++)
{
min = 1000;
j = 1; k = 0;
while (j<G.Vertices.length) //循环全部顶点
{
if(lowcost[j]!=0&&lowcost[j]<min)
{
min = lowcost[j];
k=j; //将当前最小值的下标存入k,k存储下一步的结点的标记
}
j++; //??为什么要j++
} //得到min{{生成树的顶点的集合到其他各个顶点的最短距离}
++t;
if(k<adjvex[k])
{
closeVertex[t].v1=k;
closeVertex[t].v2=adjvex[k];
closeVertex[t].weight=G.edge[adjvex[k]][k];
}
else
{
closeVertex[t].v1=adjvex[k];
closeVertex[t].v2=k;
closeVertex[t].weight=G.edge[adjvex[k]][k];
}
lowcost[k] = 0; //将当前顶点的权值设置为0,表示该顶点已经完成任务
for (j =1; j <G.Vertices.length; j++) { //循环所有顶点
if (lowcost[j]!=0&&G.edge[k][j]<lowcost[j])
{
//lowcost[j]!=0保证不会重复访问
lowcost[j]=G.edge[k][j];
adjvex[j] = k; //将下标为k的顶点存入adjvex,也就是存储下一步的最短边的起点
}
} //得到目前已经是生成树的顶点的集合到其他各个顶点的最短距离
}
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "AdjMGraph.h"
/*
功能:
主函数
参数:
argc - argv 数组的长度,大小至少为 1,argc - 1 为命令行参数的数量。
argv - 字符串指针数组,数组长度为命令行参数个数 + 1。其中 argv[0] 固定指向当前
所执行的可执行文件的路径字符串,argv[1] 及其后面的指针指向各个命令行参数。
例如通过命令行输入 "C:\hello.exe -a -b" 后,main 函数的 argc 的值为 3,
argv[0] 指向字符串 "C:\hello.exe",argv[1] 指向字符串 "-a",argv[2] 指向字符串 "-b"。
返回值:
成功返回 0, 失败返回 1
*/
int main(int argc, char* argv[])
{
// 使用第一个参数输入待处理文件的名称,若没有输入此参数就报告错误
if(argc < 2)
{
printf("Usage: app.exe filename\n");
return 1;
}
// 打开待处理的文件。读取文件中的内容。
FILE* file = fopen(argv[1], "rt");
if(NULL == file)
{
printf("Can not open file \"%s\".\n", argv[1]);
return 1;
}
AdjMGraph G;
CreateGraph(&G, file );
for(int i=0;i<G.Vertices.length;i++){
char c;
ListGet(G.Vertices, i, &c);
if(i==G.Vertices.length-1)
printf("%c",c); //最后一个元素后面的空格不输出
else
printf("%c ",c);
}
printf("\n");
PrintGraph(G );
int i=0;
int* visited= (int*)malloc(sizeof(int)*G.Vertices.length);
VDataType* output= (VDataType*)malloc(sizeof(VDataType)*G.Vertices.length);
int count=0;
for( i=0;i<G.Vertices.length;i++)
visited[i]=0;
DepthFSearch(G,0 ,visited,output,&count);
for(int i=0;i<count;i++){
if(i==count-1)
printf("%c",output[i]); //最后一个元素后面的空格不输出
else
printf("%c ",output[i]);
}
printf("\n");
for( i=0;i<G.Vertices.length;i++)
visited[i]=0;
count=0;
BroadFSearch(G,0,visited,output,&count);
for(int i=0;i<count;i++){
if(i==count-1)
printf("%c",output[i]); //最后一个元素后面的空格不输出
else
printf("%c ",output[i]);
}
printf("\n");
MinSpanTreeEage* closeVertex = malloc(sizeof(MinSpanTreeEage)*(G.Vertices.length));
Prime(G,closeVertex);
//printf("%c ",closeVertex[0].vertex);
for( i=1;i<G.Vertices.length;i++)
{
char data1,data2;
ListGet(G.Vertices,closeVertex[i].v1,&data1);
ListGet(G.Vertices,closeVertex[i].v2,&data2);
printf("%c %c %d\n",data1,data2,closeVertex[i].weight);
}
//
free(visited);
free(output);
fclose(file);
return 0;
}
要注意在prime算法里面要比较好最小生成二叉树中两个结点的大小,再存入数组,不然会出现顺序出错的情况!!!