/*
无向网图的DFS和BFS遍历及最小生成树Prim、Kruskal算法
输出结果:
请输入顶点和边的个数:6 9
请输入第1个顶点的信息:v0
请输入第2个顶点的信息:v1
请输入第3个顶点的信息:v2
请输入第4个顶点的信息:v3
请输入第5个顶点的信息:v4
请输入第6个顶点的信息:v5
请输入第1条边(Vi, Vj)两个顶点的下标及权值:0 1 10
请输入第2条边(Vi, Vj)两个顶点的下标及权值:0 2 50
请输入第3条边(Vi, Vj)两个顶点的下标及权值:0 5 30
请输入第4条边(Vi, Vj)两个顶点的下标及权值:1 4 60
请输入第5条边(Vi, Vj)两个顶点的下标及权值:2 3 40
请输入第6条边(Vi, Vj)两个顶点的下标及权值:2 5 20
请输入第7条边(Vi, Vj)两个顶点的下标及权值:3 5 70
请输入第8条边(Vi, Vj)两个顶点的下标及权值:3 4 90
请输入第9条边(Vi, Vj)两个顶点的下标及权值:4 5 80
v0 v1 v2 v3 v4 v5
v0 ∞ 10 50 ∞ ∞ 30
v1 10 ∞ ∞ ∞ 60 ∞
v2 50 ∞ ∞ 40 ∞ 20
v3 ∞ ∞ 40 ∞ 90 70
v4 ∞ 60 ∞ 90 ∞ 80
v5 30 ∞ 20 70 80 ∞
DFS: v0 v1 v4 v3 v2 v5
BFS: v0 v1 v2 v5 v4 v3
请输入最小生成树的起始顶点:2
Prim: (2, 5)20-->(5, 0)30-->(0, 1)10-->(2, 3)40-->(1, 4)60-->end
Kruskal:(0, 1)10-->(2, 5)20-->(0, 5)30-->(2, 3)40-->(1, 4)60-->end
请按任意键继续. . .
*/
# include <stdio.h>
# include <stdlib.h>
# define MAX_VEX 10//最大顶点个数
# define INF 0x3f3f3f3f//无穷大
# define SIZE 5//顶点信息最大字符串个数+1
typedef char VertexType[SIZE];
typedef int EdgeType;
typedef struct{
VertexType vexs[MAX_VEX];//顶点数组
EdgeType arc[MAX_VEX][MAX_VEX];//边表
int vexNum, edgeNum;//图中顶点和边的个数
}MGraph;//无向网图邻接矩阵
struct minEdge{
int adjvex;//顶点下标
int lowcost;//最小权值
}minEdge[MAX_VEX];//创建最小权值边数组
typedef struct{
int begin;//边起始顶点
int end;//边尾部顶点
int weight;//权值
}EdgeNode;
bool visited[MAX_VEX];//访问标志数组
void CrateMGraph(MGraph * G);//创建无向网图
void PrintMGraph(MGraph G);//打印邻接矩阵
void DFSTraverse(MGraph G);//深度优先遍历
void DFS(MGraph G, int i);//深度优先递归
void BFSTraverse(MGraph G);//广度优先遍历
void Prim(MGraph G, int start);//最小生成树-Prim算法
void Kruskal(MGraph G);//最小生成树-Kruskal算法
void InitEdge(MGraph G, EdgeNode * Edge);//初始化Edge边数组
void sort(EdgeNode * Edge, int n);//排序
int Find(int * parent, int m);//找根节点
int main(void)
{
int start;
MGraph G;
CrateMGraph(&G);
PrintMGraph(G);
DFSTraverse(G);
BFSTraverse(G);
printf("请输入最小生成树的起始顶点:");
scanf("%d", &start);
printf("Prim:\t");
Prim(G, start);
printf("end\n");
printf("\nKruskal:");
Kruskal(G);
printf("end\n");
system("pause");
return 0;
}
//创建无向网图
void CrateMGraph(MGraph * G)
{
int i, j, k, w;
printf("请输入顶点和边的个数:");
scanf("%d %d", &G->vexNum, &G->edgeNum);
for(i = 0; i < G->vexNum; i++)
{
printf("请输入第%d个顶点的信息:", i+1);
scanf("%s", &G->vexs[i]);
}
for(i = 0; i < G->vexNum; i++)
for(j = 0; j < G->vexNum; j++)
G->arc[i][j] = INF;
for(k = 0; k < G->edgeNum; k++)
{
printf("请输入第%d条边(Vi, Vj)两个顶点的下标及权值:", k+1);
scanf("%d %d %d", &i, &j, &w);
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];
}
}
//打印邻接矩阵
void PrintMGraph(MGraph G)
{
int i, j;
for(i = 0; i < G.vexNum; i++)
printf("\t%s", G.vexs[i]);
printf("\n");
for(i = 0; i < G.vexNum; i++)
{
printf("%s\t", G.vexs[i]);
for(j = 0; j < G.vexNum; j++)
{
if(G.arc[i][j] == INF)
printf("∞\t");
else
printf("%d\t", G.arc[i][j]);
}
printf("\n");
}
}
//深度优先遍历
void DFSTraverse(MGraph G)
{
int i;
for(i = 0; i < G.vexNum; i++)
visited[i] = false;
printf("\nDFS:\t");
for(i = 0; i < G.vexNum; i++)
{
if(!visited[i])
DFS(G, i);
}
printf("\n");
}
//深度优先递归
void DFS(MGraph G, int i)
{
int j;
visited[i] = true;
printf("%s\t", G.vexs[i]);
for(j = 0; j < G.vexNum; j++)
{
if(!visited[j] && G.arc[i][j] != INF)
DFS(G, j);
}
}
//广度优先遍历
void BFSTraverse(MGraph G)
{
int i , j;
for(i = 0; i < G.vexNum; i++)
visited[i] = false;
int Queue[MAX_VEX];//队列存放顶点对应下标
int front = 0, rear = 0;//队首队尾下标
printf("BFS:\t");
for(i = 0; i < G.vexNum; i++)
{
if(!visited[i])
{
visited[i] = true;
printf("%s\t", G.vexs[i]);
rear = (rear + 1) % MAX_VEX;
Queue[rear] = i; //将此顶点下标入队
while(rear != front)//当队列不为空
{
front = (front + 1) % MAX_VEX;
i = Queue[front];//队首元素出队赋值给i,便于寻找一下个邻接点
for(j = 0; j < G.vexNum; j++)
{
if(!visited[j] && G.arc[i][j] != INF)//找到未被访问的顶点并且属于当前顶点i的邻接点
{
visited[j] = true;
printf("%s\t", G.vexs[j]);
rear = (rear + 1) % MAX_VEX;
Queue[rear] = j;//将找到顶点循环依次序入队
}
}
}
}
}
printf("\n\n");
}
//最小生成树-Prim算法
void Prim(MGraph G, int start)
{
int i;
for(i = 0; i < G.vexNum; i++)
{
minEdge[i].adjvex = start;
minEdge[i].lowcost = G.arc[start][i];
}
minEdge[start].lowcost = 0;//lowcost数组值为0表示该下标对应顶点已经并入集合
for(i = 0; i < G.vexNum - 1; i++)//n个顶点最小生成树共n-1条边
{
int j = 0;
int k = 0;
int min = INF;
//循环找寻最小权值
while(j < G.vexNum)
{
if(minEdge[j].lowcost != 0 && minEdge[j].lowcost < min)
{
min = minEdge[j].lowcost;
k = j;
}
j++;
}
printf("(%d, %d)%d-->", minEdge[k].adjvex, k, minEdge[k].lowcost);
minEdge[k].lowcost = 0;//将lowcost数组值置0表示该下标对应顶点已经并入集合
//更新lowcost数组值,若新点位k的邻接点有最小权值边则替换
for(j = 0; j < G.vexNum; j++)
{
if(minEdge[j].lowcost != 0 && G.arc[k][j] < minEdge[j].lowcost)
{
minEdge[j].lowcost = G.arc[k][j];
minEdge[j].adjvex = k;
}
}
}
}
//最小生成树-Kruskal算法
void Kruskal(MGraph G)
{
int i, j;
EdgeNode * Edge = (EdgeNode*)malloc(G.edgeNum * sizeof(EdgeNode));
if(!Edge)
exit(-1);
int * parent = (int*)malloc(G.vexNum * sizeof(int));
if(!parent)
exit(-1);
//初始化parent数组判断是否构成回路
for(i = 0; i < G.vexNum; i++)
parent[i] = -1;
InitEdge(G, Edge);
sort(Edge, G.edgeNum);//对边数组按权值升序排序
int vex1, vex2, cnt;
for(cnt = 0, i = 0; i < G.edgeNum; i++)
{
vex1 = Find(parent, Edge[i].begin);
vex2 = Find(parent, Edge[i].end);
//判断是否构成环路,若两个顶点不具有相同根则不构成回路
if(vex1 != vex2)
{
printf("(%d, %d)%d-->", Edge[i]);
parent[vex2] = vex1;//表示此顶点在生成树集合中
cnt++;
if(cnt == G.vexNum - 1)
break;
}
}
free(Edge);
Edge = NULL;
free(parent);
parent = NULL;
}
//初始化Edge边数组
void InitEdge(MGraph G, EdgeNode * Edge)
{
int k = 0;
for(int i = 0; i < G.vexNum; i++)
{
for(int j = 0; j < G.vexNum; j++)
{
if(i < j && G.arc[i][j] != INF)
{
Edge[k].begin = i;
Edge[k].end = j;
Edge[k].weight = G.arc[i][j];
k++;
}
}
}
}
//查找根结点
int Find(int * parent, int m)
{
while(parent[m] > -1)
m = parent[m];
return m;
}
//排序
void sort(EdgeNode * Edge, int n)
{
EdgeNode Temp;
for(int i = 0; i < n - 1; i++)
{
for(int j = 0; j < n - 1 - i; j++)
{
if(Edge[j].weight > Edge[j+1].weight)
{
Temp = Edge[j];
Edge[j] = Edge[j+1];
Edge[j+1] = Temp;
}
}
}
}