图的算法——最小生成树,最短路径

图的算法——最小生成树,最短路径

一.最小生成树

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 算法

  1. 设置两点顶点的集合U 和T,集合U中存放已找到最短路径的顶点,集合T中存放当前还未找到的最短路径的顶点。
  2. 初始状态时,集合U中只包含源点,设为V0。
  3. 然后从集合T中选择到源点V0路径长度最短的顶点U加入到集合U中。
  4. 集合U中每加入一个新的顶点U都要修改源点V0带集合T中剩余顶点的当前最短路径值,集合T中各顶点的新的当前最短路径长度值,为原来的当前最短路径长度值与从源点过顶点U到达该顶点的带权路径长度中的较小者。
  5. 回到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];
                    }
                }
            }
        }
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值