数据结构与算法整理9——图的相关操作(C语言)
目录
图的相关理论知识见上一篇博客:https://blog.csdn.net/weixin_40695088/article/details/104429653
1、构造邻接矩阵,实现拓扑排序
使用的图为:
运行结果如下:
代码如下:
/* 用邻接矩阵表示图的拓扑排序算法*/
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 100
#define TRUE 1
#define FALSE 0
typedef char VexType;
typedef int AdjType;
typedef struct {
int n; /* 图的顶点个数 */
/*VexType vexs[MAXVEX];*/ /* 顶点信息 */
AdjType arcs[MAXVEX][MAXVEX]; /* 边信息 */
} GraphMatrix;
typedef struct {
/*VexType vexs[MAXVEX];*/ /* 顶点信息 */
int vexsno[MAXVEX]; /* 顶点在顶点表中的下标值 */
} Topo;
/* 求出图中所有顶点的入度 */
/* 方法是搜索整个邻接表 */
void findInDegree(GraphMatrix* g, int *inDegree) {
int i, j;
for (i = 0; i < g->n; i++) {
inDegree[i] = 0;
for (j = 0; j < g->n; j++)
if (g->arcs[j][i]) ++inDegree[i];
}
}
void makeNewAOV(GraphMatrix* g, int p, int* indegree, int* top) {
int k;
for (k = 0; k < g->n; k++)/* 删除以该顶点为起点的边 */
if (g->arcs[p][k]) {
indegree[k]--;
if (indegree[k] == 0) { /* 将新的入度为零的边入栈 */
indegree[k] = *top;
*top = k;
}
}
}
int topoSort(GraphMatrix * paov, Topo * ptopo) {
int i, j, nodeno = 0, top = -1;
int indegree[MAXVEX];
findInDegree(paov, indegree); /* 求出图中所有顶点的入度 */
for (i = 0; i < paov->n; i++)
if (indegree[i] == 0) { /* 将入度为零的顶点入栈 */
indegree[i] = top; top = i;
}
while (top != -1) { /* 栈不为空 */
j = top;
top = indegree[top]; /* 取出当前栈顶元素 */
/*ptopo->vexs[nodeno] = paov->vexs[j];*/ /* 将该元素输出到拓扑序列中 */
ptopo->vexsno[nodeno++] = j;
/* 取该元素边表中的第一个边结点 */
/* 删除该结点,构造新的AOV网 */
/* 对indegree数组进行修改 */
makeNewAOV(paov, j, indegree, &top);
}
if (nodeno < paov->n) { /* AOV网中存在回路 */
printf("The aov network has a cycle\n");
return FALSE;
}
return TRUE;
}
/* 按题目要求构造邻接矩阵,可以根据自己的要求修改邻接矩阵*/
GraphMatrix* makeMatrix(){
GraphMatrix* p;
int i,j;
p = (GraphMatrix*)malloc(sizeof(GraphMatrix));
p->n = 9;
for (i = 0; i < p->n; i++)
for (j = 0; j < p->n; j++)
p->arcs[i][j] = 0;
p->arcs[0][2] = 1;
p->arcs[0][7] = 1;
p->arcs[1][2] = 1;
p->arcs[1][3] = 1;
p->arcs[1][4] = 1;
p->arcs[2][3] = 1;
p->arcs[3][5] = 1;
p->arcs[3][6] = 1;
p->arcs[4][5] = 1;
p->arcs[7][8] = 1;
p->arcs[8][6] = 1;
return p;
}
Topo topo;
int main(){
int i;
GraphMatrix* p;
p = makeMatrix();
char a[9][20];
for(i=0;i<9;i++){
gets(a[i]);
}
printf("*******************\n");
if (topoSort(p, &topo) == TRUE)
for (i = 0; i < p->n; i++)
printf("%d %s \n", i,a[topo.vexsno[i]]);
return 0;
}
2、编制一个程序,使得输入道路路口(结点)n和双向交叉道路(边)时,不仅可以满足改造道路中必包含紧邻岳麓山的麓山南路,还可以满足改造道路尽量少(最小生成树)且分值最大的道路的分值尽量小,程序输出结果需包括改造的道路数及分值最大的道路的分值。
问题分析:
- (1)问题要求改造道路尽可能小,因此需实现最小生成树;
- (2)因为有一条边必须加入,故所得的生成树不一定是边的权值总和最小的生成树,在生成邻接矩阵后,需要修改必须加入的那条边的权值为0以确保权值为0的边一定出现在最小生成树中,在满足此条件的基础上去实现最小生成树;
- (3)为输出分值最大的道路的分值,我们把分值看作每条道路的权值,首先提取出最小生成数中的边的权值ki,通过for循环,将最小生成树中每条边的权值ki与必须改造的道路(边)的分值(权值w)依次进行比较,最终得到ki(0<i<n)和w这个并集中的最大值,输出分值最大的道路的分值。
具体步骤:
- (1)输入顶点、边数;
- (2)生成邻接矩阵;
- (3)修改必须加入的那条边的权值为0;
- (4)利用克鲁斯卡尔算法生成最小生成树;
- (5)取出生成树的边最大权值n;
- (6)将必须加入的那条边的权值与目标生成的最小生成树的权值不断进行比较,取出最大值并返回。
克鲁斯卡尔构造最小生成树过程:
题目所给例子(其中路口1、路口4之间的道路为必须改造的边)
此为题目中所给例子中最后得到的最小生成树。
用不同的数据来测试程序(其中路口2、路口3之间的道路为必须改造的边)
克鲁斯卡尔算法应用于此题的主要步骤:
- 先将必须改造的边的权值设为0;
- 找到最小的权值的边,加入到生成树的边集中;
- 继续比较图中其他边的权值,找到权值最小的并加入生成树 的边集中;
- 以此类推,最后得到包含必须改造边的最小生成树;
代码如下:
#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 20
#define MaxVertices 100
#define MaxWeight 32767
enum{FALSE,TRUE};
typedef char VexType;
typedef int AdjType;
typedef struct {
int n; /* 图的顶点个数 */
/*VexType vexs[MAXVEX]; 顶点信息 */
AdjType arcs[MAXVEX][MAXVEX];
int m; /* 边信息 */
} GraphMatrix;
typedef struct {
int start_vex, stop_vex; /* 边的起点和终点 */
AdjType weight; /* 边的权 */
} Edge;
Edge mst[5];
typedef struct{ //包含权的邻接矩阵的的定义
int Vertices[MaxVertices]; //顶点信息的数组
int Edge[MaxVertices][MaxVertices]; //边的权信息的数组
int numV; //当前的顶点数
int numE; //当前的边数
//int m;
}AdjMatrix;
GraphMatrix CreateGraph(AdjMatrix *G,int n,int e) //图的生成函数
{
int vi,vj,w,i,j;
G->numV=n;G->numE=e;
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;
}
for(i=0;i<G->numE;i++)
{
scanf("%d%d%d",&vi,&vj,&w);
//若为不带权值的图,则w输入1
//若为带权值的图,则w输入对应权值
G->Edge[vi-1][vj-1]=w;//①
G->Edge[vj-1][vi-1]=w;//②
//无向图具有对称性的规律,通过①②实现
//有向图不具备此性质,所以只需要①
}
scanf("%d%d",&vi,&vj);
int w0;
w0=G->Edge[vi-1][vj-1];
G->Edge[vi-1][vj-1]=0;//①
G->Edge[vj-1][vi-1]=0;//②
GraphMatrix graph;
graph.n=n;
graph.m=w0;
int k1,k2;
for(k1=0;k1<n;k1++){
for(k2=0;k2<n;k2++){
graph.arcs[k1][k2]=G->Edge[k1][k2];
}
}
return graph;
}
int maxpower(GraphMatrix graph, Edge mst[]){
int i,j=0,k=0;
if (kruskal(graph,mst) == TRUE)
for (i = 0; i < graph.n-1; i++){
//printf("(%d %d %.0f)\n", mst[i].start_vex, mst[i].stop_vex, mst[i].weight);
j= mst[i].weight;
if(k < j){
k=j;
}
}
return k;
}
int kruskal(GraphMatrix graph, Edge mst[])
{
int i, j, num = 0, start, stop;
float minweight;
int* status = (int *)malloc(sizeof(int)*graph.n);
for (i = 0; i < graph.n; i++)
status[i] = i;
while (num < graph.n - 1){
minweight = MaxWeight;
for (i = 0; i < graph.n-1; i++)
for (j = i+1; j < graph.n; j++)
if (graph.arcs[i][j] < minweight){
start = i; stop = j;
minweight = graph.arcs[i][j];
}
if (minweight == MaxWeight) return FALSE;/* 不能得到最小生成树*/
/* 加入start和stop组成的边不产生回路*/
if (status[start] != status[stop]){
mst[num].start_vex = start;
mst[num].stop_vex = stop;
mst[num].weight = graph.arcs[start][stop];
num++;
j = status[stop];
for (i = 0; i < graph.n; i++)
if(status[i] == j)
status[i] = status[start];
}
/* 删除start和stop组成的边*/
graph.arcs[start][stop] = MaxWeight;
}
return TRUE;/* 能得到最小生成树*/
}
int main(){
AdjMatrix G;
GraphMatrix graph;
int n,e,k1;
scanf("%d%d",&n,&e);
graph=CreateGraph(&G,n,e);
printf("%d\t",n-1);//整改的道路数量
kruskal(graph,mst);
k1=maxpower(graph,mst);
if(k1>graph.m){
printf("%d",k1);
}
else
printf("%d",graph.m);
}