コード例 #1
0
def span_forest(graph):
    vnum = graph.get_num()
    DFS_seq = [None] * vnum

    #递归主体
    def clfs(graph, v):
        nonlocal DFS_seq
        for u, w in graph.out_edge(v):  #遍历节点v的所有的临接边
            if DFS_seq[u] is None:  #寻找生成树不包含的临接顶点
                DFS_seq[u] = (v, w)  #将u加入到生成树的点集合中
                clfs(graph, u)  #然后以u作为下次递归的起点,寻找u的临接点,找到没有包含
                #在生成树中的临接点,然后将其加入生成树,依次递归。

    #选择生成树的起点,因为有可能某个起点并不是根
    for i in range(vnum):
        if DFS_seq[i] is None:
            DFS_seq[i] = (i, 0)
            clfs(graph, i)
    #检查是否有孤立点,即加入了两个起点其将所有的点检索完毕
    counter = 0
    for x in DFS_seq:
        if x[1] == 0:
            counter += 1
    if counter > 1:
        raise ValueError('There is a islanddot')
    return DFS_seq
コード例 #2
0
def toposort(graph):

    vnum = graph.get_num()  #取顶点数值
    indegree, toposeq = [0] * vnum, []  #建立入度表,其中度为0的顶点串成一串,拓扑序列表,初始为空表
    zerov = -1  #第一个入也是相当于栈底入度为0的顶点的元素,代表往下没有入度为0的顶点了,它是第一个
    for vi in range(vnum):
        for j, w in graph.out_edge(vi):  #遍历所有顶点的临接边,然后做出所有顶点的入度表
            if w != 0:  #该条件排除对角线上自身为入度的干扰
                indegree[j] += 1
    for vi in range(vnum):  #找到入度为0的顶点,然后将他们串成串,具体的是:第一个入度为0的点元素为-1,栈底,更新zerov
        if indegree[vi] == 0:  #第二个入度为0的点的元素是上一个的顶点的序号zerov,循环。因此最后出现的为0的顶点为栈顶
            indegree[
                vi] = zerov  #此时zerov指向该顶点的序号,然后每个indegree[vi]的元素都是下一个入度为0的顶点的序号
            zerov = vi

    for n in range(vnum):  #主循环
        if zerov == -1:  #没有入度为0的顶点,即zerov没有更新,那么不存在拓扑排序
            return False
        vi = zerov  #保存zerov,即第一个入度为0的顶点
        zerov = indegree[
            zerov]  #第一个入度为0的顶点出栈(从图中取下,加入拓扑序),此时栈顶应该指向下一个入度为0的顶点的序号indegree[zerov]
        toposeq.append(vi)  #将弹出的第一个入度为0的顶点加入拓扑序
        for v, w in graph.out_edge(vi):  #由于将顶点弹出,那么与该顶点相连的所有临接顶点的入度都要减一(新图)
            indegree[v] -= 1
            if indegree[
                    v] == 0:  #更新入度表后,可能出现新的入度为0的点,此时要加入栈,具体是将indegree[v]=zerov,即新点v的元素指向下一个
                indegree[
                    v] = zerov  #入度为0的点,也就是老的zerov,然后再更新zerov=v,更新栈顶,继续重复上述过程
                zerov = v

                #随着顶点不断从入读表中弹出,不断产生新的入度为0的点,进而不断弹出入度为0的元素,当弹出vnum个时,得到排序
    return toposeq
コード例 #3
0
 def ee_time(graph, toposeq):
     vnum = graph.get_num()
     ee = [0] * vnum  #初始化各事件最早发生事件为0
     for i in toposeq:  #按照拓扑序列的事件顺序遍历事件
         for j, w in graph.out_edge(i):  #以每个事件寻找其临接事件,然后更新临接事件的最早发生事件
             if ee[j] < (ee[i] + w):  #一个事件可能被多此找到,因此寻找最大的那个最早发生事件
                 ee[j] = ee[i] + w
     return ee
コード例 #4
0
 def le_time(graph, toposeq, ealast):
     vnum = graph.get_num()
     le = [ealast] * vnum
     for k in range(vnum - 2, -1,
                    -1):  #显然寻找最迟发生事件需要逆拓扑排序寻找,vnum-1最后一个的le是确定的
         i = toposeq[k]
         for j, w in graph.out_edge(i):  #找到事件i的临接事件,对其最迟发生时间更新
             if (le[j] - w) < le[i]:  #如果计算出的最迟发生时间le[i]-w,比原先的更小,那么更新它
                 le[i] = le[j] - w
     return le
コード例 #5
0
    def crt_path(graph, ee, le):  #根据ee,le和graph来寻找关键活动序列
        vnum = graph.get_num()
        crt_ac = []
        for i in range(vnum):
            for j, w in graph.out_edge(i):  #不能直接对比ee,le,因此这样只能得到关键活动,没办法得到关键路径
                if ee[i] == (le[j] - w):  #得到关键路径,遍历时间,对于时间i,它的临接时间j被遍历,如果满足
                    #ee[i]=le[j]-w  le[i]
                    crt_ac.append(
                        ((i, j),
                         (ee[i],
                          le[i])))  #输出的是vi,vj时间之间的活动集合,同时加上(ee[i],le[i])检验一下

        return crt_ac, ee, le
コード例 #6
0
def all_shaort_paths(graph):
    vnum = graph.get_num()
    a = [[graph.get_edge(i, j) for j in range(vnum)]
         for i in range(vnum)]  #复制原图的权重表
    path = [[-1 if a[i][j] == inf else j for j in range(vnum)]
            for i in range(vnum)]  #创建轨迹表
    #其元素path[i][j]表示vi到vj路径上面的下一个点

    for k in range(vnum):
        for i in range(vnum):
            for j in range(vnum):
                if a[i][j] > a[i][k] + a[k][j]:
                    a[i][j] = a[i][k] + a[k][j]
                    path[i][j] = path[i][k]  #运用了性质vi到v的最短路径的v的前一个点v·,
                    # 那么vi到v`也是最短路径,那么下一个顶点都是相同的
    return (a, path)
コード例 #7
0
def Dij_min_spanforest(graph, vo_):
    vnum = graph.get_num()  #记录长度
    paths = [None] * vnum  #轨迹记录表,同时按是不是None来区分该顶点在不在U中
    cands = Prique_heap([(0, vo_, vo_)
                         ])  #记录V-U中顶点的最短路径的优先序列,只有U中顶点的临接点在其中,不在的长度为无穷
    counter = 0  #计数器
    while counter < vnum and not cands.is_empty():
        lens, u, v = cands.dequeue()  #弹出最短路径最短的一个临接顶点,即vmin=v
        if paths[v] is not None:  #如该定点在U中不满足条件,跳过
            continue
        paths[v] = (u, lens)  #满足条件,记录该顶点,加入U,格式是上一顶点再加上路径长度
        counter += 1
        for j, w in graph.out_edge(v):  #由于vmin的加入,vmin的临接顶点的最短路径可能会发生变化,进行更新。
            if paths[j] is None:
                cands.enqueue((lens + w, v, j))  #更新加入vmin的不再U中的临接顶点及其最短路径
    return paths  #返回得到的路径表
コード例 #8
0
def P_MST(graph):
    vnum = graph.get_num()
    mst = [None] * vnum  #建立一个最小生成树的路径表,同时也充当表示最小生成树
    #已包含的顶点的作用,比如第i个元素的值不是none代表最小生成树
    #包含第i个顶点
    cands = Prique_heap([(0, 0, 0)])  #初始时将初始点的父节点(它本身)表示的临接边加入优先序列
    counter = 0  #计数值
    while counter < vnum and not cands.is_empty():
        w, vi, vj = cands.dequeue()  #从优先序列中取出权值最小的临接边(一个点在U,一个点在V-U)
        if mst[vj] is not None:  #这个临接边不满足条件vj在V-U中,也是就是一个点在生成树集之外
            continue  #跳过这条边
        mst[vj] = ((vi, vj), w)  #将满足条件的边加入到路径表,同时也表示第vj个顶点已经加入到生成树集
        counter += 1
        for j, w in graph.out_edge(vj):  #将新加入的vj的满足条件的临接边(终点在V-U之外)加入到优先队列
            #此时队列中包含V-U中的顶点的所有的还未弹出的临接边(继续找最小的)
            if mst[j] is None:
                cands.enqueue((w, vj, j))
    return mst
コード例 #9
0
def critical_paths(graph):
    def ee_time(graph, toposeq):
        vnum = graph.get_num()
        ee = [0] * vnum  #初始化各事件最早发生事件为0
        for i in toposeq:  #按照拓扑序列的事件顺序遍历事件
            for j, w in graph.out_edge(i):  #以每个事件寻找其临接事件,然后更新临接事件的最早发生事件
                if ee[j] < (ee[i] + w):  #一个事件可能被多此找到,因此寻找最大的那个最早发生事件
                    ee[j] = ee[i] + w
        return ee

    def le_time(graph, toposeq, ealast):
        vnum = graph.get_num()
        le = [ealast] * vnum
        for k in range(vnum - 2, -1,
                       -1):  #显然寻找最迟发生事件需要逆拓扑排序寻找,vnum-1最后一个的le是确定的
            i = toposeq[k]
            for j, w in graph.out_edge(i):  #找到事件i的临接事件,对其最迟发生时间更新
                if (le[j] - w) < le[i]:  #如果计算出的最迟发生时间le[i]-w,比原先的更小,那么更新它
                    le[i] = le[j] - w
        return le

    #ee,le中的顺序都是代表拓扑排序的顶点(事件)的顺序
    def crt_path(graph, ee, le):  #根据ee,le和graph来寻找关键活动序列
        vnum = graph.get_num()
        crt_ac = []
        for i in range(vnum):
            for j, w in graph.out_edge(i):  #不能直接对比ee,le,因此这样只能得到关键活动,没办法得到关键路径
                if ee[i] == (le[j] - w):  #得到关键路径,遍历时间,对于时间i,它的临接时间j被遍历,如果满足
                    #ee[i]=le[j]-w  le[i]
                    crt_ac.append(
                        ((i, j),
                         (ee[i],
                          le[i])))  #输出的是vi,vj时间之间的活动集合,同时加上(ee[i],le[i])检验一下

        return crt_ac, ee, le

    toposeq = toposort(graph)
    #print(toposeq)
    if not toposeq:  #由于不存在入度为0的点,因此graph不存在拓扑序列
        return False
    ee = ee_time(graph, toposeq)
    le = le_time(graph, toposeq, ee[graph.get_num() - 1])
    return crt_path(graph, ee, le)
コード例 #10
0
def K_MST(graph):
    vnum = graph.get_num()
    reps = [i for i in range(vnum)]  #建立一个指示表,看表示不同的顶点是否在一个连通图
    edges = []
    for i in range(vnum):  #构建包含所有边的表
        for j, w in graph.out_edge(i):
            edges.append((w, i, j))
    edges.sort()  #将按从小大的顺序排列
    mst = []  #记录最小生成图边的表,完成时应该包含n-1条边
    #counter=0                          #计数值,当计数到n-1时退出,证明已经得到最小生成树

    for w, vi, vj, in edges:
        if reps[vi] != reps[vj]:  #新加入的最小边有效的前提是端点不再一个连同图内
            mst.append(((vi, vj), w))
            #counter+=1
            if len(mst) == vnum - 1:
                break
            rep, orep = reps[vi], reps[vj]  #更新维护指示表,使新加入的边的端点在一个连通图内
            for x in range(len(reps)):
                if reps[x] == orep:
                    reps[x] = rep

    return mst