图的算法——最小生成树,最短路径
文章目录
一.最小生成树
1.概念
一个图中可能存在多条相连的边,我们一定可以从一个图中挑出一些边生成一棵树。
当图中每条边都存在权重时,这时候我们从图中生成一棵树(n - 1 条边)时,生成这棵树的总代价就是每条边的权重相加之和。
图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的。
2.实现最小生成树的两种算法
1. Prim (普里姆算法)
void MiniSpanTree_Prim(MGraph *G,VertexType start)
{
int i,j,k;
ShortEdge shortedge[VertexMax];
//1.处理起始点start
k=LocateVex(G,start);
for(i=0;i<G->vexnum;i++)
{
shortedge[i].adjvex=start;
shortedge[i].lowcost=G->AdjMatrix[k][i];
}
shortedge[k].lowcost=0;//lowcost为0表示该顶点属于U集合
//2.处理后续结点
for(i=0;i<G->vexnum-1;i++)//对集合U,去找最短路径的顶点
{
k=minimal(G,shortedge);//找最短路径的顶点
printf("%c->%c,%d\n",shortedge[k].adjvex,G->Vertex[k],shortedge[k].lowcost);//输出找到的最短路径顶,及路径权值
shortedge[k].lowcost=0;//将找到的最短路径顶点加入集合U中
for(j=0;j<G->vexnum;j++)//U中加入了新节点,可能出现新的最短路径,故更新shortedge数组
{
if(G->AdjMatrix[k][j]<shortedge[j].lowcost)//有更短路径出现时,将其替换进shortedge数组
{
shortedge[j].lowcost=G->AdjMatrix[k][j];
shortedge[j].adjvex=G->Vertex[k];
}
}
}
}
2. Kruskal算法
该算法的核心思想是归并边
int Kruskal(int n,int m){
int ans = 0;//记入最小生成树的权值
int num_Edge = 0;//边的数目
for(int i=0;i<m;i++){
int fu = find_father(ed[i].u);//找最终父亲
int fv = find_father(ed[i].v);//找最终父亲
if(fu!=fv){
father[fu] = fv;//合并
ans+=ed[i].dis;
num_Edge++;
if(num_Edge==n-1) break;
}
}
if(num_Edge!=n-1) return -1;//退出条件
else return ans;
}
二.最短路径
1.概念
在图中,不可避免要解决的一个问题就是计算两点之间的最短路径,对于图结构来说,两个点之间不一定只有一条路径,那么如何才能找出最短的那一条就是图中最短路径问题。
2.迪杰斯特拉Dijkstra 算法
- 设置两点顶点的集合U 和T,集合U中存放已找到最短路径的顶点,集合T中存放当前还未找到的最短路径的顶点。
- 初始状态时,集合U中只包含源点,设为V0。
- 然后从集合T中选择到源点V0路径长度最短的顶点U加入到集合U中。
- 集合U中每加入一个新的顶点U都要修改源点V0带集合T中剩余顶点的当前最短路径值,集合T中各顶点的新的当前最短路径长度值,为原来的当前最短路径长度值与从源点过顶点U到达该顶点的带权路径长度中的较小者。
- 回到3,此过程不断重复,直到集合T中的顶点全部加入到集合U中为止。
//邻接表实现迪杰斯特拉
/*
*start为起始顶点
*path[i]为辅助数组 表示源点v0到终点vi之间最短路径中 vi的前驱结点
*dist[i]表示起始顶点到i顶点最短路径长度
*/
void Dijkstra_AdjList(AdjListGraph G,VertexType vs,VertexType path[],int dist[]){
int i, j, k;
int min; //求取最短路径长度时使用
int index; //保存已经求出的最短路顶点在顶点表中下标
int temp; //缓存 判定路径长时防止溢出
int start = GetVexPosition(G, vs); //获取起始顶点下标
bool flag[G->vexnum]; //标记数组 标记顶点是否已经求出最短路径
/* 1.初始化三个数组 */
for (k = 0; k < G->vexnum; k++){
flag[k] = false; //初始化访问数组
path[k] = -1; //初始化vi前驱结点为-1
dist[k] = GetEdgeWeight_Index(G,start,k); //初始化start到各个顶点的路径长度
}
// start顶点自身的dist与path数组也要初始化
flag[start] = true;
path[start] = -1;
dist[start] = 0;
/* 2.试探并求取顶点间最短路 n-1趟*/
for (i = 0; i < G->vexnum - 1; i++){
//求出start到index的最短路
min = INF; //求取当前距离start最近的顶点
for (j = 0; j < G->vexnum; j++){
if(!flag[j]&&dist[j]<min){
min = dist[j]; //保存当前一轮中最小的路径长度
index = j; //保存对应最短路径长度的顶点在顶点表中下标
}
}
flag[index] = true; //标记index下标顶点已经求出当前轮的最短路
// 修正当前最短路径和前驱顶点
// 当已经求得"顶点index的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"
for (j = 0; j < G->vexnum; j++){
//加入已经求出最短路的index结点再进行试探 以便更新下一轮的dist与path
temp = GetEdgeWeight_Index(G, index, j);
temp = (temp == INF ? temp : min + temp); //防止溢出
if(!flag[j]&&temp<dist[j]){
dist[j] = temp;
path[j] = index;
}
}
}
// 打印dijkstra最短路径的结果
printf("顶点V%d到其余顶点的最短路: \n", G->List[start].data);
for (i = 0; i < G->vexnum; i++)
printf(" shortest(V%d, V%d)=%d\n", G->List[start].data, G->List[i].data, dist[i]);
}
3. Floyd算法
一次性计算出所有点之间相互的最短距离
// Floyd算法
void Floyd()
{
for (int k = 0; k < num; k++)
{
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num; j++)
{
if (map[i][j] > map[i][k] + map[k][j])
{
map[i][j] = map[i][k] + map[k][j];
}
}
}
}
}