数据结构【第十二天】:图(三)

图的应用

一、最小生成树

针对带权值的无向图,即网结构,使用n-1条边连接n个顶点,且使权值的和最小。

我们把构造连通网的最小代价生成树称为最小生成树

1.普利姆(Prim)算法

对于如下例子

输入以上的邻接矩阵,利用prim算法生成最小数

const int MAXVEX = 9;    //顶点最大数,设为9
const int INFINITY = 65535;    //最大权值

void MiniSpanTree_Prim(MGraph G)
{
    int min,i,j,k;
    int adjvex[MAXVEX];    //保存相关顶点下标,adjvex[2]=1代表顶点v2与顶点v1相连
                           //lowcost[i]表示顶点adjvex[i]与i两个顶点之间的权值
    int lowcost[MAXVEX];   //保存相关顶点间的权值,为0表示该顶点完成任务
    lowcost[0] = 0;        //初始化第一个权值,v0加入生成树
    adjvex[0] = 0;         //初始化第一个顶点的下标
    for(i=1;i<G.numVertexes;i++)
    {
        lowcost[i] = G.arc[0][i];    //将与v0相关的边的权值全存入顶点
        adjvex[i] = 0;            //初始化均为v0的下标
    } 
    for(i=1;i<G.numVertexes;i++)    //循环所有除v0外的顶点
    {
        k=0;     
        j=1;
        min = INFINITY;    //初始化权值,设为无穷
        while(j<G.numVertexes)    //循环所有结点,得到当前lowcost最小值和该值的下标
        {
            if(lowcost[j]!=0 && lowcost[j]<min)    //找到未访问过的且与v0之间权值最小的顶点
            {
                min = lowcost[j];    //获取当前lowcost数组中最小权值
                k = j;               //记录最小权值的顶点的下标值    
            }
            j++;
        }
        cout<<adjvex[k]<<k;    //打印当前顶点边中权值最小的边
        lowcost[k] = 0;        //当前顶点权值设为0,表示该顶点已经完成任务
        for(j=1;j<G.numVertexes;j++)    //循环所有结点
        {   
            //此时的lowscost还未纳入顶点k
            //若下标为k顶点的各边权值小于此前这些顶点未被加入生成树的权值
            if(lowcost[j]!=0 && G.arc[k][j]<lowcost[j])
            {
                lowcost[j] = G.arc[k][j];    //将较小的权值存入lowcost
                adjvex[j] = k;               //将下标k存入adjvex,代表j与k两个顶点
            }
        }
    }
}

核心思想:

假设N=(P,{E})是连通网,TE是N上最小生成树中边的集合。算法从U={u0}(u0属于V)TE={}开始。重复以下操作:在所有边u属于U,v属于V-U的边(u,v)中找到一条代价最小的边(u0,v0)并入集合TE,同时并入顶点v0,直到U=V。此时必有n-1条边,则T=(V,{TE})为N的最小生成树。时间算法复杂度为O(n^2).

个人看法:lowcost数组代表未加入的结点到已生成树的某个结点adjvex[i]的权重大小,最后的for循环即是更新权重步骤

2.克鲁斯卡尔算法

使用边集数组的存储形式,直接以边为目标去构建,寻找最小的权值的边来构建树,只需防止形成环路而已。

扫描二维码关注公众号,回复: 5828268 查看本文章
//边集数组的结构定义
typedef struct
{
    int begin;
    int end;
    int weight;
}Edge;

利用排序算法,将边集数组按权值从小到大排列,如下图

最小生成树的生成代码

void MiniSpanTree_Kruskal(MGraph G)
{
    int i,n,m;
    Edge edges[MAXEDGE];    //定义边集数组
    int parent[MAXEDGE];    //利用一维数组来判断是否形成环路
    /*****************/
    /*此处省略排序算法*/
    /*****************/
    for(i=0;i<G.numVertexes;i++)
    {
        parent[i] = 0;    //初始化数组为0
    }
    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
            cout<<edges[i].begin<<edges[i].end<<edges[i].weight<<endl;
        }
    }
}

int Find(int *parent,int f)    //查找连线顶点的尾部下标
{
    while(parent[f]>0)        //如果该顶点已经有了连线,则循环递推直到为0退出
        f = parent[f];        //parent[i] = j即代表已有边i-j存在
    return f;
}

核心思想:
假设N=(V,{E})是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。E选择代价最小的边,若该边依附的顶点落在T中的不同的连通分量上,则将此边加入T中,否则舍去此边而选择下一条代价最小的边。以此类推,直到T中所有顶点都在同一个连通分量上为止。其时间复杂度为O(eloge)。


二、最短路径

对于无向带权值的图,即网图而言,最短路径是指两顶点之间经过边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个为终点。

1.迪杰斯特拉算法

计算从特定点出发到其余顶点的最短路径问题

核心思想:先求v0到v1的最短距离,再求v0到v2的距离并与之前v0到v1加v1到v2的距离比较得出最短距离,依次基于之前的最短距离进行比较求出更远点的路径。

#define MAXVEX 9
#define INFINITY 65535
typedef int Pathmatirx[MAXVEX];        //存储最短路径的下标数组
typedef int ShortPathTable[MAXVEX];    //存储到各点最短路径的权值和

//针对有向网G的v0顶点到其余顶点v最短路径P[v]及带权长度D[v]
//P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和

void ShortestPath_Dijkstra(MGraph G,int v0,Pathmatirx *P,ShortPathTable *D)
{
    int v,w,k,min;
    int final[MAXVEX];    //最短路径标志位,设为1表示最短路径已求
    for(v=0;v<G.numVertexes;v++)    //初始化数据
    {
        final[v] = 0;
        (*D)[v] = G.matirx[v0][v];    //将与v0有连线的顶点加上权值
        (*P)[v] = 0;            //初始化路径数组P为0
    }
    (*D)[v0] = 0;     //v0到v0路径为0
    final[v0] = 1;    //v0到v0不需要求路径
    /*开始主循环,每次求得v0到某个v顶点的最短路径*/
    for(v=1;v<G.numVertexes;v++)
    {
        min = INFINITY;    //设当前离v0顶点的最近距离
        for(w=0;w<G.numVertexes;w++)    //寻找离v0最近的顶点
        {
            if(!final[w] && (*D)[w]<min)
            {
                k = w;                //记录顶点w记为k
                min = (*D)[w];        //顶点k离v0最近
            }
        }
        final[k] = 1;                  //将目前找到的最近的顶点设为1
        for(w=0;w<G.numVertexes;w++)    //修正比较最短的路径距离
        {
            //增加了最短距离v0-k之后,基于此更新所有的其他顶点到v0的最短路径值
            if(!fianl[w] && (min+G.matirx[k][w]<(*D)[w]))
            //说明找到了更短的路径,故修改D[w]和P[w]
                (*D)[w] = min + G.matirx[k][w];
                (*P)[w] = k; 
        }
    }
}

该算法的时间复杂度为O(n^2)

2.弗洛伊德算法

若顶点数为n,使用两个nxn的矩阵来D-1和P-1,根据以下公式

循环修改D矩阵,同时P对应矩阵也根据当前D的变化进行修改,注意D为对称矩阵,可简化计算,例子如下

算法如下

#define MAXVEX 9
#define INFINITY 65535
typedef int Pathmatirx[MAXVEX][MAXVEX];        //存储最短路径的下标数组
typedef int ShortPathTable[MAXVEX][MAXVEX];    //存储到各点最短路径的权值和

//针对有向网G的各顶点v到其余顶点w最短路径P[v][w]及带权长度D[v][w]
//P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和

void ShortestPath_Floyd(MGraph G,int v0,Pathmatirx *P,ShortPathTable *D)
{
    int v,w,k;
    for(v=0;v<G.numVertexes;v++)    //初始化D、P
    {
        for(w=0;w<G.numVertexes;w++)
        {
            (*D)[v][w] = G.matirx[v][w];    //初始化权值
            (*P)[v][w] = w;            //初始化P
        }
    }
    for(k=0;k<G.numVertexes;k++)
    {
        for(v=0;v<G.numVertexes;v++)
        {
            for(w=0;w<G.numVertexes;w++)
            {
                if((*D)[v][w]>(*D)[v][k]+(*D)[k][w])
                //如果经过下标为k的顶点的路径比原来两点之间更短
                //将两点间的权值设为更小的一个
                    (*D)[v][w] = (*D)[v][k] +(*D)[k][w];
                    (*P)[v][w] = (*P)[v][k];    //路径设置为经过下标为k的顶点
            }
        }
    }
}

//由最后的P得出最短路径
for(v=0;v<G.numVertexes;v++)
{
    for(w=v+1;w<G.numVertexes;w++)
    {
        printf("v%d - v%d weight: %d",v,w,D[v][w]);    
        k = P[v][w];            //获得第一个路径顶点下标
        printf("path :%d",v);    //打印源点
        while(k!=w)
        {
            printf(" -> %d",k);    //打印路径顶点
            k = P[v][w];    //获得下一个路径顶点下标
        }
        printf(" -> %d\n",w);    //打印终点
    }
    printf("\n");
}

以上弗洛伊德算法能算出所有顶点到所有顶点之间的最短路径,其主要就是二重循环初始化加上三重循环权值修正。但是其时间复杂度为O(n^3)。

猜你喜欢

转载自blog.csdn.net/KNOW_MORE/article/details/88578307