图的最短路径-Prim算法

  1. 算法思想

    设G=(V,E)是具有n个顶点的连通图,T=(U,TE)是G的最小生成树.其中V和U分别是图和树的定点集合,E和TE分别是图和树的边的集合.初始,T树为空,即U和TE都是空集.首先选择图中的任意顶点作为根,加入到集合U中,然后每加入一个顶点,都必须加入一条以树已有顶点和新加入顶点连成的边.这样才能既保证连通同时又不产生回路.这样的添加工作只要进行n-1次.
  2. 算法分析
    使用贪心算法,每次向中添加边(u,v)时,都能够保证边的一个顶点u属于集合U,而另外一个顶点属于V-U集合.也就是说U是V的子集,当算法结束是U=V
    该边是具有改性质的边中权值最小的边.设若|U|=k,|V|=n,则可能具有m=k*(n-k)条边,检索 m条边的时间是不可忽略的.因此我们使用了edges[N]去解决这个问题.它记录了如下信息:formvex,endvex,weight,即一条边上的两个顶点和权值.
    使用edges[N]表示边集数组,下标为0的元素不使用,以0号顶点为树根.在图G6中,顶点编号为0~6,初始值(k=0),edges[1]到edges[6]分别存放着从0号顶点到1~6号顶点的各条边,若此边确实存在,则边上的权值为实际值,否则为INF.该数组保留着从树中到数外的(n-k)条当前权值最小的边.

    表格中第k(k>=1)行表示添加第k条边后边集数组edges各个分量的当前值edges[i],i是边集数组的下标.从k-1行选取实际权值最小的边加入到树中.采取将当前最短边edges[m]与边edges[k]交换的方式,这样就把数组划分成了两个部分,下标1~k表示已经加入树的边,从k+1到n表示未加入树的边.这样每次进行修改的时候仅仅是在k+1到n范围内进行.

    分析k=1的情况:首先从k-1行中找到权值最小的边,一个顶点在树内,一个顶点在树外,该边为(0,3) 权值5 把它交换到下标为1的位置上.顶点3的加入,进行数组的更新:{(0,1) 8}由{(3,1) 3}代替;{(0,5) -}由{(3,5) 7}代替;((0,6) -)由{(3,6) 15代替}
    ……
    以下不再赘述.

  3. 代码实现

#include <stdio.h>
#include <stdlib.h>
#include <vector>

using namespace std;

const int MAX_E=1000;

typedef struct edges{
    int fromvex;
    int endvex;
    int weight;
}Edges;

Edges edges[MAX_E];
vector<int> G[MAX_E];
void prim0(){
    int i,min,m
    //初始化1~n个元素
    /*
        给边集数组赋初值,因为取0号元素为树根,可以不包含0号元素.  
    */
    for(i=1;i<n;i++){
        edges[i].fromvex = 0; //边的起始顶点为树种的0号元素
        edges[i].endvex = i; //边的终止顶点为树外的1~n号顶点
        edges[i].weight = G[0][i]; //每条边上的权值
    }

    for(i=1;i<n;i++){ //向树中添加边的循环
        min=edges[i].weight; m=i;
        for(j=i+1;j<n;j++){
            if(adges[j].weight<min){
                min=edges[j].weight; m=j;
            }
        }   //找到边集数组中,下标为m的边权值最小
        if(k!=m){
            Edges temp = edges[m]; edges[m]=edges[k]; edges[k]=temp;
        }           /*交换m和k号元素*/
        j = adges[k].endvex; //j为新加入树的第k条边的终端顶点
        for(t=i+1;t<n;t++){//对尚未加入树的n-k条边进行调整
            int s = edges[t].endvex; int e = G[j][s];
            /*若树中加入顶点j前到树外顶点s的最短边没有j到s短,则用后者更新它*/
            if(w<edges[t].weight){
                edges[s].weight = e;
                edges[s].fromvex = j;
            }
        }

    }
}   

/*
    该算法的复杂度为O(n2);
*/


//集合的理解方式

int cost[MAX_E][MAX_E]; //cist[u][v]表示边e=(u,v)的权值(不存在的情况下为INF)
int mincost[MAX_E]; //从集合X出发的边到每一个顶点的最小值
bool used[MAX_E]; //顶点i是否已经包含在生成树X中
int V; //顶点的个数

int prim1(){
    for(int i=0;i<V;i++){
        mincost[i]=INF;
        used[i]=false;
    }
    minconst[0]=0;
    used[0]=true;
    int ans=0; //最短路径长度
    while(true){
        int v=-1;
        //从不属于集合X中的顶点中选出从X到其权值最小的顶点
        for(int i=0;i<V;i++){
            if(!used[i] &&(v==-1 || mincost[i]<mincost[v]) v=i;
        }
        if(v==-1){//所有顶点都已经访问完毕,或者是没有路径
            break;
        }
        used[v]=true;   //把顶点v加入到集合X中
        ans += mincost[v]; //把长度加入到结果中
        for(int i=0;i<V;i++){
            mincost[i]=min(mincost[i],cost[v][i]);
        }
    }
    return ans;
}

/*
    该算法的复杂度仍旧为O(n2);
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值