/**
* 实验题目:
* 用破圈法求一个带权连通图的最小生成树
* 实验目的:
* 深入掌握图的复杂操作、图遍历算法和最小生成树的概念,以及
* 最小生成树的构造算法。
* 实验内容:
* 编写一个程序,用破圈法求一个带权连通图的最小生成树。
* 破圈法思路:
* 任意取一个圈,去掉圈上图中权最大的边,反复执行这个
* 步骤,直到图中没有圈为止。
*
*/
#include <stdio.h>
#include <malloc.h>
#define INF 32767 //定义∞
#define MAXV 100 //最大顶点个数
typedef char InfoType;
/*-------------------------以下定义邻接矩阵类型---------------------------*/
typedef struct
{
int no; //顶点编号
InfoType info; //顶点信息
}VertexType; //顶点类型
typedef struct
{
int edges[MAXV][MAXV]; //邻接矩阵数组(用一个二维数组存放顶点间关系(边或弧)的数据)
int n; //顶点数
int e; //边数
VertexType vexs[MAXV]; //存放顶点信息(用一个一维数组存放图中所有顶点数据)
}MatGraph; //完整的图邻接矩阵类型
//邻接表表示法-将每个顶点的邻接点串成一个单链表
/*-----------以下定义邻接表类型--------------*/
typedef struct ArcNode
{
int adjvex; //该边的邻接点编号
struct ArcNode *nextarc; //指向下一条边的指针
int weight; //该边的相关信息,如权值(用整型表示)
}ArcNode; //边结点类型
typedef struct VNode
{
InfoType info; //顶点其他信息
int cnt; //存放顶点入度,仅用于拓扑排序
ArcNode *firstarc; //指向第一条边
}VNode; //邻接表结点类型
typedef struct
{
VNode adjlist[MAXV]; //邻接表头结点数组
int n; //图中顶点数
int e; //图中边数
}AdjGraph; //完整的图邻接表类型
/*-------------------------邻接矩阵的基本运算算法---------------------------*/
/*------------由边数组A、顶点数n和边数e创建图的邻接矩阵g--------------------*/
void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e)
{
int i, j;
g.n = n;
g.e = e;
for(i = 0; i < g.n; i++)
for(j = 0; j < g.n; j++)
g.edges[i][j] = A[i][j];
}
/*------------输出邻接矩阵g--------------------*/
void DispMat(MatGraph g)
{
int i, j;
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
if(g.edges[i][j] != INF)
printf("%4d", g.edges[i][j]);
else
printf("%4s", "∞");
}
printf("\n");
}
}
/*-------------------------邻接表的基本运算算法---------------------------*/
/*-------------------由边数组A、顶点数n和边数e创建图的邻接表G--------------------*/
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
int i, j;
ArcNode *p;
G = (AdjGraph *)malloc(sizeof(AdjGraph));
for(i = 0; i < n; i++) //给邻接表中所有头结点的指针域置初值NULL
{
G->adjlist[i].firstarc = NULL;
}
for(i = 0; i < n; i++) //检查邻接矩阵中的每个元素
{
for(j = n - 1; j >= 0; j--)
{
if(A[i][j] != 0 && A[i][j] != INF) //存在一条边
{
p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
p->adjvex = j; //邻接点编号
p->weight = A[i][j]; //边的权重
p->nextarc = G->adjlist[i].firstarc; //采用头插法插入结点p
G->adjlist[i].firstarc = p;
}
}
}
G->n = n;
G->e = e;
}
/*-------------------输出邻接表G--------------------*/
void DispAdj(AdjGraph *G)
{
ArcNode *p;
for(int i = 0; i < G->n; i++)
{
p = G->adjlist[i].firstarc;
printf("顶点%d: ", i);
while(p != NULL)
{
printf("%3d[%d]->", p->adjvex, p->weight); //邻接点编号[权重]
p = p->nextarc;
}
printf("∧\n");
}
}
/*-------------------销毁图的邻接表G--------------------*/
void DestroyAdj(AdjGraph *&G)
{
ArcNode *pre, *p;
for(int i = 0; i < G->n; i++)
{
pre = G->adjlist[i].firstarc; //pre指向第i个单链表的首结点
if(pre != NULL)
{
p = pre->nextarc;
while(p != NULL) //释放第i个单链表的所有边结点
{
free(pre);
pre = p;
p = p->nextarc;
}
free(pre);
}
}
free(G); //释放头结点数组
}
#include <stdbool.h>
#define MAXE 100 // 最多的边数
typedef struct {
int u; // 边的起始顶点
int v; // 边的终止顶点
int w; // 边的权值
}Edge;
int visited[MAXV]; // 全局变量
/*------------------采用邻接矩阵存储的图g中从顶点v出发进行深度优先遍历---------------*/
static void MDFS(MatGraph g, int v)
{
int w;
visited[v] = 1; // 置访问标记
for(w = 0; w < g.n; w++) // 找顶点v的所有邻接点
{
if((g.edges[v][w] != 0) && (g.edges[v][w] != INF) && (visited[w] == 0))
{
MDFS(g, w); // 找顶点v的未访问过的邻接点w
}
}
}
/*-----------------判定图g的连通性-------------------*/
static bool connect(MatGraph g)
{
bool flag = true;
int k;
for(k = 0; k < g.n; k++)
{
visited[k] = 0;
}
MDFS(g, 0);
for(k = 0; k < g.n; k++)
{
if(visited[k] == 0)
{
flag = false;
}
}
return flag;
}
/*------------------求图g的最小生成树---------------------*/
static void spantree(MatGraph &g)
{
int i, j, k = 0, e;
Edge temp;
Edge edges[MAXE];
for(i = 0; i < g.n; i++) // 获取图中所有边信息
{
for(j = 0; j < i; j++)
{
if((g.edges[i][j] != 0) && (g.edges[i][j] != INF))
{
edges[k].u = i;
edges[k].v = j;
edges[k].w = g.edges[i][j];
k++;
}
}
}
for(i = 1; i < g.e; i++) // 将edges数组按w递减排序
{
if(edges[i].w > edges[i - 1].w)
{
temp = edges[i];
j = i - 1;
do
{
edges[j + 1] = edges[j];
j--;
}while((j >= 0) && (edges[j].w < temp.w));
edges[j + 1] = temp;
}
}
k = 0;
e = g.e;
while(e >= g.n)
{
g.edges[edges[k].u][edges[k].v] = INF; // 删除第k条边
g.edges[edges[k].v][edges[k].u] = INF;
if(connect(g)) // 若连通,则删除
{
e--;
printf(" (%d)删除边(%d,%d):%d\n", g.e - e, edges[k].u, edges[k].v, edges[k].w);
}
else // 若不连通,则恢复第k条边
{
g.edges[edges[k].u][edges[k].v] = edges[k].w;
g.edges[edges[k].v][edges[k].u] = edges[k].w;
}
k++;
}
g.e -= e; // 修改图中的边数
}
int main(void)
{
MatGraph g;
int A[MAXV][MAXV] = {
{0, 28, INF, INF, INF, 10, INF}, {28, 0, 16, INF, INF, INF, 14},
{INF, 16, 0, 12, INF, INF, INF}, {INF, INF, 12, 0, 22, INF, 18},
{INF, INF, INF, 22, 0, 25, 24}, {10, INF, INF, INF, 25, 0, INF},
{INF, 14, INF, 18, 24, INF, 0}
}; // 图的邻接矩阵二维数组表示法
int n = 7; // 图顶点数
int e = 9; // 图边数
CreateMat(g, A, n, e); // 建立图的邻接矩阵
printf("图G的邻接矩阵:\n");
DispMat(g); // 输出邻接矩阵
printf("产生最小生成树的过程:\n");
spantree(g);
printf("最小生成树如下:\n");
DispMat(g); // 输出邻接矩阵
return 0;
}
测试结果:
图G的邻接矩阵:
0 28 ∞ ∞ ∞ 10 ∞
28 0 16 ∞ ∞ ∞ 14
∞ 16 0 12 ∞ ∞ ∞
∞ ∞ 12 0 22 ∞ 18
∞ ∞ ∞ 22 0 25 24
10 ∞ ∞ ∞ 25 0 ∞
∞ 14 ∞ 18 24 ∞ 0
产生最小生成树的过程:
(1)删除边(1,0):28
(2)删除边(6,4):24
(3)删除边(6,3):18
最小生成树如下:
0 ∞ ∞ ∞ ∞ 10 ∞
∞ 0 16 ∞ ∞ ∞ 14
∞ 16 0 12 ∞ ∞ ∞
∞ ∞ 12 0 22 ∞ ∞
∞ ∞ ∞ 22 0 25 ∞
10 ∞ ∞ ∞ 25 0 ∞
∞ 14 ∞ ∞ ∞ ∞ 0