大话数据结构之图(下)
Posted -恰饭第一名-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大话数据结构之图(下)相关的知识,希望对你有一定的参考价值。
一、最小生成树
构造连通网的最小代价生成树称为最小生成树
一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边
1.1、普里姆算法
#define MAXVEX 20
#define GRAPH_INFINITY 65535
/* Prim算法生成最小生成树 */
void MiniSpanTree_Prim(MGraph G)
{
int min, i, j, k;
int adjvex[MAXVEX]; /* 保存相关顶点下标 */
int lowcost[MAXVEX]; /* 保存相关顶点间边的权值 */
lowcost[0] = 0;/* 初始化第一个权值为0,即v0加入生成树 */
/* lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */
/*之后凡是lowcost数组中的值被设置为0就是表示此下标的顶点被纳入最小生成树*/
adjvex[0] = 0; /* 初始化第一个顶点下标为0 */
/*下面的for循环表示读取邻接矩阵的第一行数据。
将数组赋值给lowcost数组,
所以此时lowcost数组值为{0,10,65535,65535,11,65535,65535,65535},
而arjvex则全部为0,此时,
我们已经完成了整个初始化的工作,准备开始生成*/
for(i = 1; i < G.numVertexes; i++) /* 循环除下标为0外的全部顶点 */
{
lowcost[i] = G.arc[0][i]; /* 将v0顶点与之有边的权值存入数组 */
adjvex[i] = 0; /* 初始化都为v0的下标 */
}
/*下面整个for循环就是构造最小生成树的过程*/
for(i = 1; i < G.numVertexes; i++)
{
min = GRAPH_INFINITY; /* 初始化最小权值为∞, */
/* 通常设置为不可能的大数字如32767、65535等 */
j = 1;k = 0;
while(j < G.numVertexes) /* 循环全部顶点 */
{
if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值小于min */
{
min = lowcost[j]; /* 则让当前权值成为最小值 */
k = j; /* 将当前最小值的下标存入k */
}
j++;
}
printf("(%d, %d)\\n", adjvex[k], k);/* 打印当前顶点边中权值最小的边 */
lowcost[k] = 0;/* 将当前顶点的权值设置为0,表示此顶点已经完成任务 */
for(j = 1; j < G.numVertexes; j++) /* 循环所有顶点 */
{
if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j])
{/* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值 */
lowcost[j] = G.arc[k][j];/* 将较小的权值存入lowcost相应位置 */
adjvex[j] = k; /* 将下标为k的顶点存入adjvex */
}
}
}
}
假设 N=( V,{ E })是连通网,TE 是 N 上最小生成树中边的集合。
算法从 U ={ u0 } (u0 ∈ V),TE={ } 开始,重复执行下述操作:在所有 u ∈ U, v ∈ V-U 的边(u,v) ∈ E 中找一条代价最小的边(u0,v0)并入集合 TE,同时 v0 并入 U,直至 U = V 为止。
此时 TE 中必有 n-1 条边,则 T =(V,{ TE })为 N 的最小生成树
1.2、克鲁斯卡尔算法
基本思想:假设连通网N=(V,{E}),则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{ }),图中每个顶点自成一个连通分量。
在E中选择权值最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去此边而选择下一条权值最小的边。依此类推,直至所有顶点都在同一连通分量上为止。
typedef struct
{
int begin;
int end;
int weight;
}Edge; /* 对边集数组Edge结构的定义 */
/* 生成最小生成树 */
void MiniSpanTree_Kruskal(MGraph G)
{
int i, j, n, m;
int k = 0;
int parent[MAXVEX];/* 定义一数组用来判断边与边是否形成环路 */
Edge edges[MAXEDGE];/* 定义边集数组,edge的结构为begin,end,weight,均为整型 */
/* 用来构建边集数组并排序********************* */
for ( i = 0; i < G.numVertexes-1; i++)
{
for (j = i + 1; j < G.numVertexes; j++)
{
if (G.arc[i][j]<GRAPH_INFINITY)
{
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = G.arc[i][j];
k++;
}
}
}
sort(edges, &G);
/* ******************************************* */
for (i = 0; i < G.numVertexes; i++)
parent[i] = 0; /* 初始化数组值为0 */
printf("打印最小生成树:\\n");
for (i = 0; i < G.numEdges; i++) /* 循环每一条边 */
{
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].end);
if (n != m) /* 假如n与m不等,说明此边没有与现有的生成树形成环路 */
{
parent[n] = m; /* 将此边的结尾顶点放入下标为起点的parent中。 */
/* 表示此顶点已经在生成树集合中 */
printf("(%d, %d) %d\\n", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}
/* 查找连线顶点的尾部下标 */
int Find(int *parent, int f)
{
while ( parent[f] > 0)
{
f = parent[f];
}
return f;
}
二、最短路径
最短路径,是指两顶点经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点
2.1、迪杰斯特拉算法
这是一个按路径长度递增的次序产生最短路径的算法
#define MAXVEX 20
#define GRAPH_INFINITY 65535
typedef int Patharc[MAXVEX]; /* 用于存储最短路径下标的数组 */
typedef int ShortPathTable[MAXVEX];/* 用于存储到各点最短路径的权值和 */
/* Dijkstra算法,求有向网G的v0顶点到其余顶点v的最短路径P[v]及带权长度D[v] */
/* P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和 */
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *P, ShortPathTable *D)
{
int v,w,k,min;
int final[MAXVEX];/* final[w]=1表示求得顶点v0至vw的最短路径 */
/*final数值是为了v0到某顶点是否已经求得最短路径的标记,如果v0到vw已经有结果,则final[w]=1*/
for(v=0; v<G.numVertexes; v++) /* 初始化数据 */
{
final[v] = 0; /* 全部顶点初始化为未知最短路径状态 */
(*D)[v] = G.arc[v0][v];/* 将与v0点有连线的顶点加上权值 */
(*P)[v] = -1; /* 初始化路径数组P为-1 */
}
/*上面的for循环是在对数据进行初始化的工作。此时final数值值均为0,表示所有的点都未求得最短路径*/
(*D)[v0] = 0; /* v0至v0路径为0 */
final[v0] = 1; /* v0至v0不需要求路径 */
/* 开始主循环,每次求得v0到某个v顶点的最短路径 */
for(v=1; v<G.numVertexes; v++)
{
min=GRAPH_INFINITY; /* 当前所知离v0顶点的最近距离 */
for(w=0; w<G.numVertexes; w++) /* 寻找离v0最近的顶点 */
{
if(!final[w] && (*D)[w]<min)
{
k=w;
min = (*D)[w]; /* w顶点离v0顶点更近 */
}
}
final[k] = 1; /* 将目前找到的最近的顶点置为1 */
for(w=0; w<G.numVertexes; w++) /* 修正当前最短路径及距离 */
{
/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
if(!final[w] && (min+G.arc[k][w]<(*D)[w]))
{ /* 说明找到了更短的路径,修改D[w]和P[w] */
(*D)[w] = min + G.arc[k][w]; /* 修改当前路径长度 */
(*P)[w]=k;
}
}
}
}
最终返回的数组D和数组P,是可以得到v0到任意一个顶点的最短路径和路径长度
三、拓扑排序
在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网
所谓拓扑排序,其实就是对一个有向图构造拓扑序列的过程。构造时会有两个结果,如果此网的全部顶点都被输出,则说明它是不存在环(回路)的AOV网;如果输出顶点数少了,哪怕是少了一个,也说明这个网存在(回路),不是AOV网
3.1、拓扑排序算法
- 从AOV网图中选择一个入度为0的顶点输出,然后删去此顶点并且输出它
- 删去从该顶点发出的全部有向边
- 重复上述两步,直到剩余的网中不再存在没有前驱的顶点为止
/* 邻接表结构****************** */
typedef struct EdgeNode /* 边表结点 */
{
int adjvex; /* 邻接点域,存储该顶点对应的下标 */
int weight; /* 用于存储权值,对于非网图可以不需要 */
struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;
typedef struct VertexNode /* 顶点表结点 */
{
int in; /* 顶点入度 */
int data; /* 顶点域,存储顶点信息 */
EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes,numEdges; /* 图中当前顶点数和边数 */
}graphAdjList,*GraphAdjList;
/* **************************** */
在算法中,我们还需要辅助的数据结果一栈,用来存储处理过程中入度为0的顶点,目的是为了避免每个查找时都要去遍历顶点表栈找有没有入度为0的顶点
/* 拓扑排序,若GL无回路,则输出拓扑排序序列并返回1,若有回路返回0。 */
Status TopologicalSort(GraphAdjList GL)
{
EdgeNode *e;
int i,k,gettop;
int top=0; /* 用于栈指针下标 */
int count=0;/* 用于统计输出顶点的个数 */
int *stack; /* 建栈将入度为0的顶点入栈 */
stack=(int *)malloc(GL->numVertexes * sizeof(int) );
for(i = 0; i<GL->numVertexes; i++)
if(0 == GL->adjList[i].in) /* 将入度为0的顶点入栈 */
stack[++top]=i;
while(top!=0)
{
gettop=stack[top--];
printf("%d -> ",GL->adjList[gettop].data);
count++; /* 输出i号顶点,并计数 */
for(e = GL->adjList[gettop].firstedge; e; e = e->next)
{
k=e->adjvex;
if( !(--GL->adjList[k].in) ) /* 将i号顶点的邻接点的入度减1,如果减1后为0,则入栈 */
stack[++top]=k;
}
}
printf("\\n");
if(count < GL->numVertexes)
return ERROR;
else
return OK;
}
以上是关于大话数据结构之图(下)的主要内容,如果未能解决你的问题,请参考以下文章