图算法---单源最短路径

开篇

写了几篇记录学习图算法,Want先生很不耐烦的提出了自己的问题:他妈的,你扯了那么多算法,我的要求很简单,告诉我从点s到点v有几条路可以到达?其中那条路妹子最多?他妈的要是我敢时间那条路最快?

或许这篇文章可以回答want先生的问题,当然也有许多aspire先生,你写的这些文章对我一点用都没有,我希望的文章是能直接解决我遇到的问题,对此,我很抱歉,希望你能找到灵感,看到一坨屎也能产生灵感,如果是的,我很高兴这是那坨屎。

最短路径最优子结构(开门见山)

算法导论引理24.1(最短路径的子路径是最短路径):对于一给定的带权有向图G=(V,E),所定义的权函数w:E --->  R。设P=(v1,v2,...,vk)的最短路径是从v1到vk的最短路径,对于任意i,j,其中1 <= i <= j  <= K,
设Pij = (Vi,Vi+1,....,Vk)为P从顶点vi到vj的子路径。那么,Pij是从Vi到Vj的最短路径。
证明:反证,假设Pij不是Vi到Vj的最短路径。

一个最短路径能包含回路吗(最短路径上的顶点会重复吗)?分三种情况考虑:负权回路、正权回路、0权回路。
1、负权回路(不存在)
      那么将不存在V1到Vk的最短路径,因为在这个回路上无限循环,最短路径是“负无穷”(我自己认为这种情况不存在回路,单如果限制要求路径上的点不能重复的话,那么是有最短路径的)
2、正权回路(不存在)
       显然把回路那部分去掉,有比这更短的路径
3、0权回路(不存在)
       显然,把0权回路去掉,就是无回路的最短路径。

松弛技术

 对加权有向图G=(V,E),对每个顶点v属于V,都设置一个属性d[v],用来描述从原点s到v的字段路径上权值的上届,称为最短路径估计(松弛技术的理论基础)

最短路径以及松弛的性质:

三角不等式性质:

 对任意边(u,v)属于边集E,有 min(s,u) < =  min(s,u) + w(u,v)

上届性质:

对任意顶点v属于V,有d[v] >= min(s,v),而且一旦d[v] 达到min(s,v)值就不在改变

无路径性质:

如果从s到v不存在路径,则总是有d[v] = min(s,v) =正无穷

收敛性质:-

如果路径(s,...,u,v)是s到v的最短路径,而且在松弛边(u,v)之前的任何时间d[u] = min(s,u),则在松弛边(u,v)后总有d[v] = min(s,v)

路径松弛性质(本人认为是最直观的性质,可以根据直接写出算法的性质):

如果路径p=(v1,...,vk)是v1到vk的最短路径,而且p按照(v1,v2)(v2,v3)...(Vk-1,Vk)的顺序进行松弛,那么d[k] = min(v1,vk)。这个性质保持并不受其他松弛操作的影响,即使他们与p的边上的松弛操作混合在一起也是一样的。

 24.1 Bellman-Ford算法

1、首先介绍一下顶点的G的类
class G:
    vector = []#数组,用邻接表方式存储图,用顶点在数组位置标识顶点
    d = []#存储顶点的距离
    p =[]#存储顶点的父母
2、初始化函数
#初始化,将所有顶点的父母置为NULL,将所有顶点v的d[v]设为无穷,将s的d[s]设为0
def Initialize_single_source(G1,s):
    for u in range(0,len(G1.vector)):
        G1.d.append(None)#adas
        G1.p.append(None)
    G1.d[s] = 0
    return
3、松弛函数
#对边(u,v),进行松弛操作,w为计算边的权重函数
def relax(u,v,G1,w):
    weight = w(u,v)
    if comp_d(G1.d[u],weight,G1.d[v]):
        G1.d[v] = G1.d[u] + weight
        G1.p[v] = u
    return

4、bellman-ford算法
      4.1、首先初始化图G=(V,E)
      4.2、@1:进行|V|-1次循环操作,见下面
               @2:遍历所有的边,对边进行松弛操作
      4.3、 判断是否有负权回路,有返回False,无返回True
      4.4、复杂度O(V*E)
      
def bellman_ford(G1,w,s):
    Initialize_single_source(G1,s)
    for n in range(0,len(G1.vector)-1):
        for u in range(0,len(G1.vector)):
            for v in G1.vector[u]:
                relax(u,v,G1,w)
    for u in range(0,len(G1.vector)):
            for v in G1.vector[u]:
                weight = w(u,v)
                if comp_d(G1.d[u],weight,G1.d[v]):
                    return False
    return True
5、习题
    5.1、对bellman-ford算法进行修改,对任意顶点v,当从源顶点v的某些路径下存在一个负权回路,则设置d[v]为负无穷。
            个人思路: 对4.3步进行修改,如果d[u] + w(u,v) < d[v],则将u的后裔 和u的d设置为负无穷
    5.2、设G=(V,E)为一带权有向图,其权函数w:E--->R。请给出一个O(VE)时间的算法,对每个顶点v属于V,找出 min(min(u,v)),u属于V。
            个人思路:  假设图没有回路

24.2有向无回路图中的单源最短路径

1、算法分析:
      1.1、进行拓扑排序
      1.2、@1:安照拓扑顺序执行下面操作
       @2:遍历顶点u的出边,进行松弛(relax)操作
2、证明有向无回路图,进行拓扑排序后(v0,v1,v2,....,vk),不存在边(vi,vj)其中i > j
      证明:假设存在这样的边(vi,vj)
                由拓扑排序可知f[vi]  <  f[vj]
                由于存在边(vi,vj),所以有d[vi]  > d[vj](因为如果d[vi]  < d[vj],则 f[vi]  >  f[vj],与实际不符)
                根据深度优先搜索定理,可知vi是vj的后裔,所以vj可到达vi
                再加上边(vi,vj),这就是一条回路

3、有向无回路图中的单源最短路径---算法

      

def dag_top_sort(G1,w,s):
    ret_sort = top_sort(G1)
    Initialize_single_source(G1,s)
    for u in ret_sort:
        for v in G1.vector[u]:
            relax(u,v,G1,w)
    print G1.vector
    print G1.p
    print G1.d
4、习题

    4.1、给出一个高效算法来统计有向无回路图中的全部路径数。分析所给的算法


24.3 Dijkstra 算法(有向图,但是所有边的权重必须大于等于0)

1、算法介绍
       1.1、初始化
       1.2、将顶点以d为key插入最小优先队列
       1.3、@1:不断获取队列中d最小的顶点u
                @2:对顶点u的边(u,v),进行松弛,改变d[v]的值

2、Dijkstra 是典型的贪心算法,即这个性质路径p(v0,v1,...,vk)是v0到vk的最短路径,由于每条边的权重大于0所以,min(v0,vi)<=  min(v0,vj),其中i < j。根据这一性质,我们每一步找出最优选择,最终就可以得到解

class node:
    def __init__(self,key,index):
        self.key1 = key
        self.index = index
def cmpItem(x,y):
    if x.key1 == None:
        return False
    if y.key1 == None:
        return True
    if x.key1 < y.key1:
        return True
    return False
def Dijkstra(G1,w,s):
    Initialize_single_source(G1,s)
    queue1 = prioty_queue(cmpItem)
    for i in range(0,len(G1.vector)):
        queue1.insert(node(G1.d[i],i))
    while len(queue1.data):
        u = queue1.getMin()
        for v in G1.vector[u.index]:
            relax(u.index,v,G1,w)
            queue1.decrease(v,G1.d[v])
    return

3、习题
  3.1、已知有一有向图G=(V,E),其每条边(u,v)均对应一个权重值r(u,v),表示顶点u到顶点v之间通信线路的可靠性。取值范围为0 <= r(u,v)<= 1。并假设这些概率是独立的,写出一个有效算法,来找出两个指定顶点间最可靠线路。
  个人思路:(最短路径是权重的叠加,这里是权重的相乘,形无常形,举一反三)
          假设有一条v0到vk的最可靠线路(v0,v1,。。。,vk),则存在可靠性p(v0,vi) >=  p(v0,vj) (i < j ),根据这一性质,我们可以采用DIJKSTRA算法。
          松弛操作:用d[v]表示s到顶点v最高可靠概率下届,假设存在边(u,v)   d[v] < d[u]*r(u,v),则d[v] = d[u]*r(u,v)
          
def newCmp(n1,n2):
    if n1.key1 > n2.key1:
        return True
    return False
def get_max_access(G1,s,e):
    for n in range(0,len(G1.vector)):
        G1.p.append(None)
        G1.d.append(0)
    G1.d[s] = 1
    queue1 = prioty_queue(newCmp)
    for n in range(0,len(G1.vector)):
        queue1.insert(node(G1.d[n],n))
    while len(queue1.data):
        u = queue1.getMin()
        if u.index  == e:
            break
        for v in G1.vector[u.index]:
            newWeight = ((u.index + v)%5+1)/5.0
            if newWeight > G1.d[v]:
                G1.d[v] = newWeight
                G1.p[v] = u.index
                queue1.decrease(v,G1.d[v])
    cc = e
    print cc
    while G1.p[cc] != None:
        cc = G1.p[cc]
        print cc
    return

  3.2、设G =(V,E)为带权有向图,权函数w:E--->(0,1,...,w),其中W为某非负整数。修改Dijkstra算法,以使其计算从指定源点s的最短路径所需的运行时间为O((V+E)logW)
          
      个人思路: 上面实现的最小优先队列的执行时间为logV,在这里我们可以改进优先队列的操作,使其每个操作的执行时间为logW


你可能感兴趣的:(图算法---单源最短路径)