数据结构(5) -- 图

5.4 图的应用

图的应用主要包括:最小生成树、最短路径、拓扑排序和关键路径

5.4.1 最小生成树(MST)

连通图的生成树:图的极小连通子图,包含图中所有顶点,只含尽可能少的边。若砍去最小生成树的一条边,使生成树变成非连通图,加上一条边,会形成一条回路。

最小生成树:边权值最小的那棵生成树

  1. 最小生成树不唯一,但权值唯一,且是最小的。

  2. 算法:prim算法/kruskal算法,都是基于贪心算法

  3. prim算法:

    1. 假设N={V,E}是连通网,ET是N上最小生成树边的集合。算法从VT-{u0},ET={}开始,重复执行:在所有u∈VT,v∈V-VT的边(u0,v0)∈E中找一条权值最小的边,将此边加入ET,将v0加入VT,知道VT=V为止。

    2. 该算法时间复杂度O(|v2|),不依赖E,适用于求解边稠密的图的最小生成树

  4. kruskal算法:

    1. 初始化:VT=V,ET=∅,每个顶点构成一棵独立的树

    2. 循环:按G的边权递增顺序依次从E-ET选一条边,若加入边后不构成回路,则加入ET,否则舍弃。

    3. 通常采用堆来存放边的集合,每次选择最小权值的边只需要O(log|E|),可以采用并查集的数据结构表示T,构造T的复杂度为O(|E|log|E|)

    4. 适合边稀疏,顶点较多的图

5.4.2 最短路径

带权路径长度最短的路径称最短路径。求解最短路径的算法通常依赖一性质:两点之间的最短路径也包含了路径上其他顶点间的最短路径。一般分单源最短路径(某点到其他点的最短路径)和每对顶点间的最短路径。

问题:对任意给出的图G(V,E)和起点s,终点t,如何求S到T的最短路径。

解决最短路径问题的常用算法有 Dijkstra算法、Bellman-Ford算法、SPFA算法和Floyd算法。

1. Dijkstra算法

思想:对图G设定集合S,存放已经访问过的集合。然后从V-S中选择与起点s距离最短的顶点记为u,访问并加入集合S。之后,令顶点u为中介点,优化起点s与所有从u出发能到达的顶点v之间的最短距离。执行n次,直到集合S包含所有顶点。

算法具体实现:

(1)集合S可以用一个bool数组vis[]表示,当vis[i]=true时表示顶点Vi已被访问。

(2)令int型数组d[]表示起点s到达顶点Vi的最短距离,起始时除了起点s的d[s]=0,其余顶点都赋为一个很大的数。

Dijkstra也基于贪心策略,时间复杂度O(|V2|),若要求解每对顶点之间的最短距离,需要对每个顶点运行一次dijkstra算法,复杂度O(|V3|)

注:Dijkstra只能应对所有边权都是非负数的情况,如果边权出现负数,那么Dijkstra算法可能会出错。

//伪代码
Dijkstra(G,d[],s){
    初始化;
    for(循环n次){
    u = 使d[u]最小的还未被访问的顶点的标号;
    记u被访问;
    for(从u出发能到达的所有顶点v){
        if(v未被访问&&以u为中介使s到顶点v的最短距离d[v]更优){
            优化d[v];
        }
    }
}

2. Bellman-Ford算法和SPFA算法

Bellman-Ford算法(BF算法)可解决单源最短路径问题,但能处理有负边权的情况。

现在考虑环,从某个顶底出发,经过若干不同的顶点之后可以回到该顶点的情况。根据环中边的边权之和的正负分零环、正环、负环。零环和正环不会影响最短路径的求解;而如果有负环,且从源点可以到达,就会影响求解,但如果负环无法从源点出发到达,则最短路径的求解不会受到影响。

思想:BF算法设置一个数组d,存放从源点到达各个顶点的最短距离。同时BF算法返回一个bool值:若存在从源点可到达的负环,返回false;否则返回true,此时数组d存放的值就是从源点到达各顶点的最短距离。

具体实现:

对图中边进行V-1轮操作,每轮都遍历图中所有的边:对每条边u->v,若以u为中介点能使d[v]更小,就更新。

for(i = 0; i < n-1; i++){
    for(each edge u—>v){
        if(d[u]+length[u->v] < d[v]){
            d[v] = d[u] + length[u->v];
        }
    }
}

此时,若无从源点可到达的负环,则数组d所有值都达到了最优。因此,只需再对所有边进行一轮操作,判断是否有某条边u->v仍然可以更新,若有,则说明图中有从源点可达的负环,返回false;否则,说明d数组已达到最优。

BF算法时间复杂度为O(VE)

 

虽然BF算法思路简洁,但时间复杂度很高。BF算法每轮操作都操作所有的边,显然有很多无意义的操作。注意到,只有当某个顶点u的d[u]值改变时,从它出发的边的邻接点v的d[v]值才有可能改变。由此可以进行优化:建立一个队列,每次取出队首u,对从u出发的边进行松弛。如果可松弛则进行覆盖。如果v不在队列,则加入队列。这样操作直到队空(说明无从源点可达的负环),或某个顶点的入队次数超过v-1,说明有可达负环。这样优化后的算法叫做SPFA算法。它期望的时间复杂度是O(kE)

queue<int> Q;
源点s入队;
while(队非空){
    取出队首元素u;
    for(u的所有邻接边u->v){
        if(d[u]+dis<d[v]){
            d[v] = d[u]+dis;
            if(v不在Q){
                v入队;
                if(v入队次数大于n-1){
                    说明有可达负环,return;
                }
            }
        }
    }
}

3. Floyd算法

解决全源最短路问题。时间复杂度O(n^3),限制顶点数n在200以内。

枚举顶点k∈[1,n]
    以顶点k为中介点,枚举所有顶点对i和j(i∈[1,n],j∈[1,n])
        如果 dis[i][k]+dis[k][j] < dis[i][j]成立
            赋值 dis[i][j] = dis[i][k]+dis[k][j]  

5.4.3 拓扑排序

有向无环图:若一个有向图中不存在环,称DAG图

AOV网:DAG表示一个工程,顶点表示活动,有向边<vi, vj>表示活动vi必须先于vj进行。称顶点表示活动的网络,记为AOV网,拓扑排序复杂度O(|V|+|E|)

AOE网:在带权有向图中,以顶点表示事件,有向边表示活动,边上的权值表示完成该活动所需开销。

最大路径:源点到汇点的所有路径中,具有最大路径长度的路径称关键路径,关键路径上的活动称关键活动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值