JHHK

欢迎来到我的个人网站
行者常至 为者常成

【进阶】11、图(五)

目录

最短路径(Shortest Path)

一、有权图

最短路径是指两顶点之间权值之和最小的路径(有向图、无向图均适用,不能有负权环

img

二、无权图

无权图相当于是全部边权值为1的有权图

img

三、负权边

有负权边,但没有负权环时,存在最短路径

img

A到E的最短路径是:A → B → E

四、负权环

有负权环时,不存在最短路径

img

通过负权环, A到E的路径可以无限短
A → E → D → FE → D → FE → D → FE → D → F → E → ……

五、最短路径

最短路径的典型应用之一:路径规划问题

求解最短路径的3个经典算法

单源最短路径算法
✓ Dijkstra(迪杰斯特拉算法)
✓ Bellman-Ford(贝尔曼-福特算法)

多源最短路径算法
✓ Floyd(弗洛伊德算法)

Dijkstra(迪杰斯特拉算法)

一、Dijkstra

属于单源最短路径算法,用于计算一个顶点到其他所有顶点的最短路径

使用前提:不能有负权边
时间复杂度:可优化至 O(ElogV) ,E 是边数量,V 是节点数量

由荷兰的科学家 Edsger Wybe Dijkstra 发明,曾在1972年获得图灵奖

img

二、等价思考

Dijkstra 的原理其实跟生活中的一些自然现象完全一样
把每1个顶点想象成是1块小石头
每1条边想象成是1条绳子,每一条绳子都连接着2块小石头,边的权值就是绳子的长度
将小石头和绳子平放在一张桌子上(下图是一张俯视图,图中黄颜色的是桌子)

img

接下来想象一下,手拽着小石头A,慢慢地向上提起来,远离桌面
B、D、C、E会依次离开桌面
最后绷直的绳子就是A到其他小石头的最短路径

img

有一个很关键的信息
后离开桌面的小石头,都是被先离开桌面的小石头拉起来的

三、执行过程

执行过程一

img

执行过程二

img

松弛操作(Relaxation):更新2个顶点之间的最短路径
这里一般是指:更新源点到另一个点的最短路径
松弛操作的意义:尝试找出更短的最短路径
确定A到D的最短路径后,对DC、DE边进行松弛操作,更新了A到C、A到E的最短路径

执行过程三

img

四、代码实现

/// begin到各顶点的最短路径(value:LCPathInfo)
/// @param begin 起始顶点
-(NSDictionary<id,LCPathInfo*>*)shortestPath:(id)begin{
    NSDictionary * dic = [self __dijkstra:begin];
    return dic;
}

/// dijkstra算法返回value:LCPathInfo
/// @param begin 起始点
-(NSDictionary<id,LCPathInfo*>*)__dijkstra:(id)begin{

    LCVertex* beginVertex = [self __verticsGetKey:begin];
    if (beginVertex == nil) return nil;


    //value:LCPathInfo  存放已经离开桌面的顶点(起始点到vertex的权重值,已经最终确定)
    NSMutableDictionary<id,LCPathInfo*> * selectedPaths = [NSMutableDictionary dictionary];

    //vertex:LCPathInfo 存放可能离开桌面的顶点(起始点到vertex的权重值,还未最终确定)
    NSMutableDictionary<LCVertex*,LCPathInfo*> * paths = [NSMutableDictionary dictionary];

    //初始化paths
    [paths setObject:[[LCPathInfo alloc] initWithWeight:self.weightManager.zero]
              forKey:beginVertex];
   


    while (paths.count) {

        //从可能离开的顶点中,找到将要离开桌面的顶点
        pair<LCVertex*, LCPathInfo*> minEntry = [self __getMinPath:paths];

        //离开桌面
        LCVertex* minVertex = minEntry.first;       //vertex
        LCPathInfo* minPathInfo = minEntry.second;  //pathInfo
        id minValue = minVertex.value;              //value


        //value:pathInfo 放入已选
        [selectedPaths setObject:minPathInfo forKey:minValue];
        
        NSLog(@"selectedPaths = %@",selectedPaths);

        //vertex:pathInfo 从待选中移除
        [paths removeObjectForKey:minVertex];

        // 对minVertex的outEdges进行松弛操作
        for (LCEdge* edge in minVertex.outEdges) {
            
            //如果已经被选择,不再重复进行选择
            if ([selectedPaths.allKeys containsObject:edge.to.value]) continue;
            
            LCPathInfo * fromPathInfo = minPathInfo;
            LCPathInfo * toPathInfo   = [paths objectForKey:edge.to];
            if (toPathInfo == nil) {
                toPathInfo = [[LCPathInfo alloc] init];
                [paths setObject:toPathInfo forKey:edge.to];
            }
            
            //对某一条进行松弛操作
            [self __relaxEdge:edge fromPathInfo:fromPathInfo toPathInfo:toPathInfo];
        }
        
    }

    //去除起始点
    [selectedPaths removeObjectForKey:begin];

    return selectedPaths;
}



/// 从NSDictionary<LCVertex*,LCPathInfo*>*获取最小权重的pair<LCVertex*, LCPathInfo*>返回
/// @param paths NSDictionary<LCVertex*,LCPathInfo*>*
-(pair<LCVertex*, LCPathInfo*>)__getMinPath:(NSDictionary<LCVertex*,LCPathInfo*>*)paths{
    
    __block LCVertex    * minVertex;
    __block LCPathInfo  * minPathInfo;
    
    //vertex:weight
    [paths enumerateKeysAndObjectsUsingBlock:^(LCVertex* key, LCPathInfo* pathInfo, BOOL * stop) {
        if (minPathInfo == nil || [self.weightManager compareE1:pathInfo.weight e2:minPathInfo.weight]<0) {
            minPathInfo = pathInfo;
            minVertex = key;
        }
    }];
    
    return pair<LCVertex*, id>(minVertex,minPathInfo);
}

/// 对某一条边进行松弛操作,松弛成功返回true,失败返回false
/// @param edge 边
/// @param fromPathInfo 边的起始pathInfo对象
/// @param toPathInfo 边的终止pathInfo对象
-(BOOL)__relaxEdge:(LCEdge*)edge
      fromPathInfo:(LCPathInfo*)fromPathInfo
        toPathInfo:(LCPathInfo*)toPathInfo{
    
    id fromWeight = fromPathInfo.weight;
    if (fromWeight == nil) return false;
    id newWeight = [self.weightManager addE1:fromWeight e2:edge.weight];
    
    //松弛操作
    id toWeight   = toPathInfo.weight;
    if (toWeight == nil || [self.weightManager compareE1:newWeight e2:toWeight]<0) {
        toPathInfo.weight = newWeight;
        [toPathInfo.edgeInfos removeAllObjects];
        [toPathInfo.edgeInfos addObjectsFromArray:fromPathInfo.edgeInfos];
        [toPathInfo.edgeInfos addObject:edge.info];
        return true;
    }else{
        return false;
    }
}


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.