图的应用
一、图的连通性问题
(一)无向图的连通分量
在对图遍历时,对于连通图,无论是DFS还是BFS,只需要调用一次搜索过程,只需要从任意一个顶点出发,就可以遍历图中给每一个顶点。
对于非连通图,需要多次调用搜索过程,每次调用所得到的顶点为每个连通分量中的顶点集。调用搜索的过程的次数就是该图连通分量的个数。
(二)两个顶点之间的简单路径
判断顶点u到v是否有简单路径
从u开始进行DFS或BFS搜索,若访问到v则u,v间有简单路径
int pre[];//记录从第u个顶点到v的简单路径
void EasyPath(Graph *G,int u,int v){
pre=(int *)malloc(G->vexnum*sizeof(int));
for(int i=0;i<G->vexnum;i++){
pre[i]=-1;//对pre数组进行初始化
}
pre[u]=-2;//将pre[u]初始化为-2,表示初始顶点u已经被访问,且没有前驱
DFSPath(G,u,v);//使用深度优先搜索找到一条从u到v的简单路径
free(pre);
}
bool DFSPath(Graph *G,int u,int v){
int j;
for(j=firstadj(G,u);j>=0;j=nextadj(G,u,j)){
if(pre[j]==-1){
pre[j]=u;
if(j==v){
PrintPath(pre,v);//从v开始沿着pre输出路径u到v
return true;
}else if(DFSPath(G,j,v)){
return true;
}
}
}
return false;
}
(三)图的生成树与最小生成树
一个连通图的生成树是指一个极小连通子图,它含有图的全部顶点,但是只有已连通n个顶点的n-1条边
(1)深度优先生成树
(2)广度优先生成树
(3)最小生成树
对于连通的带权图,其生成树也是带权的,生成的树T的边的权值综和称为该树的权
W(T)=W(u,v)
最小生成树(MST):一个无向图G的最小生成树是由该图链接所有顶点的边构成的树,且总代价最小,值得注意的是MST不唯一。使用贪心选择策略,思路是先局部最优,再全局最优。
(四)普里姆Prim算法
设图G=(V,E),生成树T的顶点集为U
(1)任取图中的一个顶点v0加入集合U
(2)在所有满足u属于U,V属于V-U的边(u,v)属于E中
选择一个权重最小的边加入生成树T=T中
将v0加入到U中
(3)重复(2)直到全部n个顶点均已加入到U集合中
可见Prim算法是逐步增加U中的顶点,可称为加边法
#define MAXV
#define INF 32767
typedef struct{
int no;//顶点编号
InfoType info;//顶点信息(权重)
}VertexType;
typedef struct{
int edges[MAXV][MAXV];//邻接矩阵,元素为权重
VertexType vexs[MAXV];//存放顶点信息
int n,e;//顶点数、边数
}MatGraph;
void Prim(MaxGraph *g,int v){
int lowcost[MAXV];//记录顶点
int closest[MAXV];//记录当前顶点邻接点的权值
int i,j,k,min;
for(i=0;i<g->n;i++){//初始化lowcost何closest数组
lowcost[i]=g->edges[v][i];
closest[i]=v;
}
for(i=1;i<g->n;i++){
min=INF;
//找最小权值
for(j=0;j<g->n;j++){//在(V-U)中找出离U最近的顶点k
if(lowcost[j]!=0 && lowcost[j]<min){
min=lowcost[j];
k=j;//k记录最近顶点编号
}
}
printf("(%d,%d)-%d",closest[k],k,min);
lowcost[k]=0;//赋值为0,标记k已经加入U
for(j=0;j<g->n;j++){//修改数组lowcost和closest
if(lowcost[j]!=0 && g->edges[k][j]<lowcost[j]){//把原有邻接表的权重和lowcost数组的权重
lowcost[j]=g->edges[k][j];
closest[j]=k;
}
}
}
}
Prim思路 局部最优+调整 = 全局最优(贪心算法)其算法时间复杂度为o(n^2)