JHHK

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

【进阶】12、图(六)

目录

Bellman-Ford

一、算法原理

Bellman-Ford 也属于单源最短路径算法,支持负权边,还能检测出是否有负权环
算法原理:对所有的边进行 V – 1 次松弛操作( V 是节点数量),得到所有可能的最短路径
时间复杂度:O(EV) ,E 是边数量,V 是节点数量

下图的最好情况是恰好从左到右的顺序对边进行松弛操作
对所有边仅需进行 1 次松弛操作就能计算出A到达其他所有顶点的最短路径

img

最坏情况是恰好每次都从右到左的顺序对边进行松弛操作
对所有边需进行 V – 1 次松弛操作才能计算出A到达其他所有顶点的最短路径

img

二、实例

img

img

img

三、代码实现

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

/// bellford算法返回value:LCPathInfo
/// @param begin 起始点
-(NSDictionary<id,LCPathInfo*>*)__bellmanFord:(id)begin{
    
    LCVertex* beginVertex = [self __verticsGetKey:begin];
    if (beginVertex == nil) return nil;


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

    
    //对每个edge进行v-1次松弛,v为顶点数量
    int time = (int)self.vertics.count-1;
    
    while (time--) {

        //NSSet的输出顺序是随机的,
        //但只要这个set对象不发生改变,其输出顺序永远是这个顺序
        for (LCEdge* edge in self.edges) {
            
            LCPathInfo* fromPathInfo = [selectedPaths objectForKey:edge.from.value];
            if (fromPathInfo == nil) {
                fromPathInfo = [[LCPathInfo alloc] init];
                [selectedPaths setObject:fromPathInfo forKey:edge.from.value];
            }
            
            LCPathInfo* toPathInfo = [selectedPaths objectForKey:edge.to.value];
            if (toPathInfo == nil) {
                toPathInfo = [[LCPathInfo alloc] init];
                [selectedPaths setObject:toPathInfo forKey:edge.to.value];
            }
            
            //对某一条边进行松弛操作
            [self __relaxEdge:edge fromPathInfo:fromPathInfo toPathInfo:toPathInfo];
        }
    }
    
    
    //再来一次验证是否有负权环
    for (LCEdge* edge in self.edges) {
        
        LCPathInfo* fromPathInfo = [selectedPaths objectForKey:edge.from.value];
        if (fromPathInfo == nil) {
            fromPathInfo = [[LCPathInfo alloc] init];
            [selectedPaths setObject:fromPathInfo forKey:edge.from.value];
        }
        
        LCPathInfo* toPathInfo = [selectedPaths objectForKey:edge.to.value];
        if (toPathInfo == nil) {
            toPathInfo = [[LCPathInfo alloc] init];
            [selectedPaths setObject:toPathInfo forKey:edge.to.value];
        }
        
        //对某一条边进行松弛操作
        BOOL isSuccess = [self __relaxEdge:edge
                              fromPathInfo:fromPathInfo
                                toPathInfo:toPathInfo];
        //
        if (isSuccess) {NSLog(@"有负权环");break;}
    }

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

    return selectedPaths;
}





/// 对某一条边进行松弛操作,松弛成功返回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;
    }
}

Floyd

Floyd 属于多源最短路径算法,能够求出任意2个顶点之间的最短路径,支持负权边
时间复杂度:O(V^3),效率比执行 V 次 Dijkstra 算法要好( V 是顶点数量)

算法原理
从任意顶点 i 到任意顶点 j 的最短路径不外乎两种可能
① 直接从 i 到 j
② 从 i 经过若干个顶点到 j

假设 dist(i,j) 为顶点 i 到顶点 j 的最短路径的距离
对于每一个顶点 k,检查 dist(i,k) + dist(k,j)<dist(i,j) 是否成立
✓ 如果成立,证明从 i 到 k 再到 j 的路径比 i 直接到 j 的路径短,设置 dist(i,j) = dist(i,k) + dist(k,j)
✓ 当我们遍历完所有结点 k,dist(i,j) 中记录的便是 i 到 j 的最短路径的距离

/// 多源最短路径,返回
/// {from1Value:{toValue1:LCPathInfo
///              toValue2:LCPathInfo
///              ...
///             },
///  from2Value:{toValue1:LCPathInfo
///              toValue2:LCPathInfo
///              ...
///             },
///  ...
/// }
-(NSDictionary<id,NSDictionary<id,LCPathInfo*>*>*)shortestPath{
    
    //初始化 {fromValue:{toValue:LCPathInfo}}
    NSMutableDictionary<id,NSMutableDictionary<id,LCPathInfo*>*>* paths = [NSMutableDictionary dictionary];
    
    for (LCEdge *edge in self.edges) {
        NSMutableDictionary<id,LCPathInfo*>* map = [paths objectForKey:edge.from.value];
        if (map == nil) {
            map = [NSMutableDictionary dictionary];
            [paths setObject:map forKey:edge.from.value];
        }
        
        LCPathInfo * pathInfo = [[LCPathInfo alloc] initWithWeight:edge.weight];
        [pathInfo.edgeInfos addObject:edge.info];
        [map setObject:pathInfo forKey:edge.to.value];
    }
    
    
    //检查 dist(v1,v2) + dist(v2,v3)<dist(v1,v3) 是否成立
    __weak typeof(self) weakSelf = self;
    [self.vertics enumerateKeysAndObjectsUsingBlock:^(id v2,LCVertex* vertex2, BOOL * stop) {
        [weakSelf.vertics enumerateKeysAndObjectsUsingBlock:^(id v1,LCVertex* vertex1, BOOL * stop) {
            [weakSelf.vertics enumerateKeysAndObjectsUsingBlock:^(id v3,LCVertex* vertex3, BOOL * stop) {
                
                //v1、v2、v3须是三个不同的顶点
                if ([v1 isEqual:v2] || [v1 isEqual:v3] || [v2 isEqual:v3]) return;
                
                //v1->v2
                LCPathInfo* pathInfo12 = [weakSelf __getPathInfoWithFromValue:v1
                                                                      toValue:v2
                                                                        paths:paths];
                if (pathInfo12 == nil) return;
                
                
                //v2->v3
                LCPathInfo* pathInfo23 = [weakSelf __getPathInfoWithFromValue:v2
                                                                      toValue:v3
                                                                        paths:paths];
                if (pathInfo23 == nil) return;
                
                
                //v1->v3
                LCPathInfo* pathInfo13 = [weakSelf __getPathInfoWithFromValue:v1
                                                                      toValue:v3
                                                                        paths:paths];
                if (pathInfo13 == nil) {
                    pathInfo13 = [[LCPathInfo alloc] init];
                    [[paths objectForKey:v1] setObject:pathInfo13 forKey:v3];
                }
                
                id oldWeight = pathInfo13.weight;
                id newWeight = [self.weightManager addE1:pathInfo12.weight e2:pathInfo23.weight];
                
                if (oldWeight == nil || [self.weightManager compareE1:newWeight e2:oldWeight]<0) {
                    pathInfo13.weight = newWeight;
                    [pathInfo13.edgeInfos removeAllObjects];
                    [pathInfo13.edgeInfos addObjectsFromArray:pathInfo12.edgeInfos];
                    [pathInfo13.edgeInfos addObjectsFromArray:pathInfo23.edgeInfos];
                }
                
            }];
        }];
    }];
    
    return paths;
}


/// 获取pathInfo
/// @param fromValue 起始Value
/// @param toValue 终止Value
/// @param paths NSMutableDictionary<fromValue,NSDictionary<toValue,LCPathInfo*>*>
-(LCPathInfo*)__getPathInfoWithFromValue:(id)fromValue
                                 toValue:(id)toValue
                                   paths:(NSMutableDictionary*)paths{
    NSDictionary * map = paths[fromValue];
    return map == nil?nil:map[toValue];
}


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.