算法导论 第二十二章 基本的图算法

22.1图的表示

图结构G(V,E)由顶点V和边E的集合组成,主要有邻接链表表示和邻接矩阵表示,两者的差别如下所示:



若为节点添加属性则可以通过改变节点自身的值,或者为节点添加属性或在节点中增加新的指针的方式来实现。权重也是属性的一种。

22.2 广度优先搜索

对于给定一个图中的原点,从原点出发按照到原点的距离来遍历所有的连通节点称为广度优先搜索。连接链表的算法广度优先搜索算法如下:

def ENQUEUE(Q, s):
    Q.append(s)

def DEQUEUE(Q):
    r = Q.pop(0)
    return r

WHITE, GRAY, BLACK = (0, 1, 2)

class Vertex:
    def __init__(self, u):
        self.value = u
        self.color = WHITE
        self.d = float("inf")
        self.pi = None

class Edge:
    def __init__(self, u, v):
        self.fromV = u
        self.toV = v

class EdgeList:
    def __init__(self, v):
        self.vertex = v
        self.connectedV = []

class Graph:
    def __init__(self):
        self.vertexs = {}

def CONNECT(u, v):
    return Edge(u, v)

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.value in G.vertexs:
            G.vertexs[e.fromV.value] = EdgeList(e.fromV)
        if not e.toV.value in G.vertexs:
            G.vertexs[e.toV.value] = EdgeList(e.toV)

        G.vertexs[e.fromV.value].connectedV.append(e.toV)
        G.vertexs[e.toV.value].connectedV.append(e.fromV)

def PRINTLIST(el):
    print(el.vertex.value, ":")
    for v in el.connectedV:
        print(v.value)

def PRINTGRAPH(G):
    for v in G.vertexs:
        PRINTLIST(G.vertexs[v])
        print("-----")

def BFS(G, s):
    for v in G.vertexs:
        if v != s.value:
            G.vertexs[v].vertex.color = WHITE
            G.vertexs[v].vertex.d = float("inf")
            G.vertexs[v].vertex.pi = None

    s.color = GRAY
    s.d = 0
    s.pi = None

    Q = []
    ENQUEUE(Q, s)
    while len(Q) != 0:
        u = DEQUEUE(Q)
        for v in G.vertexs[u.value].connectedV:
            if v.color == WHITE:
                v.color = GRAY
                v.d = u.d + 1
                v.pi = u
                ENQUEUE(Q, v)
        u.color = BLACK
        print(u.value, u.d)

def PRINT_PATH(G, s, v):
    if v == s:
        print(s.value)
    elif v.pi == None:
        print("no path from ", s.value, " to ", v.value, "exists")
    else:
        PRINT_PATH(G, s, v.pi)
        print(v.value)


if __name__ == "__main__":
    r = Vertex('r')
    s = Vertex('s')
    t = Vertex('t')
    u = Vertex('u')
    v = Vertex('v')
    w = Vertex('w')
    x = Vertex('x')
    y = Vertex('y')

    edges = []
    edges.append(CONNECT(r, s))
    edges.append(CONNECT(r, v))
    edges.append(CONNECT(s, w))
    edges.append(CONNECT(w, t))
    edges.append(CONNECT(w, x))
    edges.append(CONNECT(t, x))
    edges.append(CONNECT(t, u))
    edges.append(CONNECT(x, u))
    edges.append(CONNECT(x, y))
    edges.append(CONNECT(u, y))

    G = Graph()
    INITGRAPH(G, edges)
    PRINTGRAPH(G)

    print("**************")
    BFS(G, s)
    print("++++++++++++++")
    PRINT_PATH(G, s, y)
    
我们采用循环不变式:queue中保存的节点到原点的距离为最短距离。采用书上的证明其正确性,概括如下:因为广度遍历的先将d小的加入queue中,当节点第一次被发现时其pi的距离+1即为最短距离。

22.3 深度优先搜索

类似与广度优先搜索,深度优先搜索算法沿着连接的边一路查找直到所有的连接点都已经发现在原路返回查找所有节点,实现如下:

def ENQUEUE(Q, s):
    Q.append(s)

def DEQUEUE(Q):
    r = Q.pop(0)
    return r

WHITE, GRAY, BLACK = (0, 1, 2)

class Vertex:
    def __init__(self, u):
        self.value = u
        self.color = WHITE
        self.d = float("inf")
        self.f = float("inf")
        self.pi = None

class Edge:
    def __init__(self, u, v):
        self.fromV = u
        self.toV = v

class EdgeList:
    def __init__(self, v):
        self.vertex = v
        self.connectedV = []

class Graph:
    def __init__(self):
        self.vertexs = {}

def CONNECT(u, v):
    return Edge(u, v)

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.value in G.vertexs:
            G.vertexs[e.fromV.value] = EdgeList(e.fromV)
        if not e.toV.value in G.vertexs:
            G.vertexs[e.toV.value] = EdgeList(e.toV)

        G.vertexs[e.fromV.value].connectedV.append(e.toV)
        #G.vertexs[e.toV.value].connectedV.append(e.fromV)

def PRINTLIST(el):
    print(el.vertex.value, ":")
    for v in el.connectedV:
        print(v.value)

def PRINTGRAPH(G):
    for v in G.vertexs:
        PRINTLIST(G.vertexs[v])
        print("-----")

global time
time = 0
def DFS(G):
    for u in sorted(G.vertexs.keys()):
        G.vertexs[u].vertex.color = WHITE
        G.vertexs[u].vertex.pi = None
    global time
    time = 0
    for u in sorted(G.vertexs.keys()):
        if G.vertexs[u].vertex.color == WHITE:
            DFS_VISIT(G, G.vertexs[u].vertex)

def DFS_VISIT(G, u):
    global time
    time = time + 1
    u.d = time
    u.color = GRAY
    for v in G.vertexs[u.value].connectedV:
        if v.color == WHITE:
            v.pi = u
            DFS_VISIT(G, v)
    u.color = BLACK
    time = time + 1
    u.f = time
    print(u.value, u.d, u.f)

if __name__ == "__main__":
    u = Vertex('u')
    v = Vertex('v')
    w = Vertex('w')
    x = Vertex('x')
    y = Vertex('y')
    z = Vertex('z')

    edges = []
    edges.append(CONNECT(u, v))
    edges.append(CONNECT(u, x))
    edges.append(CONNECT(v, y))
    edges.append(CONNECT(w, y))
    edges.append(CONNECT(w, z))
    edges.append(CONNECT(x, v))
    edges.append(CONNECT(y, x))
    edges.append(CONNECT(z, z))

    G = Graph()
    INITGRAPH(G, edges)
    PRINTGRAPH(G)

    print("===========")
    DFS(G)

根据检索的边的连接点的不同,可以将边分为如下类型:

而且易证深度优先搜索有如下性质:

22.4 拓扑排序

拓扑排序就是无向环图的深度优先遍历中u.f值的由大到小的线性排序。

def ENQUEUE(Q, s):
    Q.append(s)

def DEQUEUE(Q):
    r = Q.pop(0)
    return r

WHITE, GRAY, BLACK = (0, 1, 2)

class Vertex:
    def __init__(self, u):
        self.value = u
        self.color = WHITE
        self.d = float("inf")
        self.f = float("inf")
        self.pi = None

class Edge:
    def __init__(self, u, v):
        self.fromV = u
        self.toV = v

class EdgeList:
    def __init__(self, v):
        self.vertex = v
        self.connectedV = []

class Graph:
    def __init__(self):
        self.vertexs = {}

def CONNECT(u, v):
    return Edge(u, v)

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.value in G.vertexs:
            G.vertexs[e.fromV.value] = EdgeList(e.fromV)
        if not e.toV.value in G.vertexs:
            G.vertexs[e.toV.value] = EdgeList(e.toV)

        G.vertexs[e.fromV.value].connectedV.append(e.toV)
        #G.vertexs[e.toV.value].connectedV.append(e.fromV)

def PRINTLIST(el):
    print(el.vertex.value, ":")
    for v in el.connectedV:
        print(v.value)

def PRINTGRAPH(G):
    for v in G.vertexs:
        PRINTLIST(G.vertexs[v])
        print("-----")

global time
time = 0
def DFS(G, TG):
    for u in sorted(G.vertexs.keys()):
        G.vertexs[u].vertex.color = WHITE
        G.vertexs[u].vertex.pi = None
    global time
    time = 0
    for u in sorted(G.vertexs.keys()):
        if G.vertexs[u].vertex.color == WHITE:
            DFS_VISIT(G, G.vertexs[u].vertex, TG)

def DFS_VISIT(G, u, TG):
    global time
    time = time + 1
    u.d = time
    u.color = GRAY
    for v in G.vertexs[u.value].connectedV:
        if v.color == WHITE:
            v.pi = u
            DFS_VISIT(G, v, TG)
    u.color = BLACK
    time = time + 1
    u.f = time
    TG.append(u)
    #print(u.value, u.d, u.f)

def TOPOLOGICAL_SORT(G):
    TG = []
    DFS(G, TG)
    return TG

if __name__ == "__main__":
    a = Vertex('a')
    b = Vertex('b')
    c = Vertex('c')
    d = Vertex('d')
    e = Vertex('e')
    f = Vertex('f')
    g = Vertex('g')
    h = Vertex('h')
    i = Vertex('i')

    edges = []
    edges.append(CONNECT(a, b))
    edges.append(CONNECT(a, d))
    edges.append(CONNECT(b, c))
    edges.append(CONNECT(d, c))
    edges.append(CONNECT(e, e))
    edges.append(CONNECT(f, g))
    edges.append(CONNECT(f, i))
    edges.append(CONNECT(g, i))
    edges.append(CONNECT(g, d))
    edges.append(CONNECT(h, i))



    G = Graph()
    INITGRAPH(G, edges)
    PRINTGRAPH(G)

    print("===========")
    TG = TOPOLOGICAL_SORT(G)
    for u in TG:
        print(u.value, u.d, u.f)

22.5 强连通分量
强连通分量其实就是环,把图G中的环归并到一起,就能将有环图变为无环图,无环图的许多算法可以被应用。如下图:

通过如下代码实现:

def ENQUEUE(Q, s):
    Q.append(s)

def DEQUEUE(Q):
    r = Q.pop(0)
    return r

WHITE, GRAY, BLACK = (0, 1, 2)

class Vertex:
    def __init__(self, u):
        self.value = u
        self.color = WHITE
        self.d = float("inf")
        self.f = float("inf")
        self.pi = None
        self.k = 0

class Edge:
    def __init__(self, u, v):
        self.fromV = u
        self.toV = v

class EdgeList:
    def __init__(self, v):
        self.vertex = v
        self.connectedV = []

class Graph:
    def __init__(self):
        self.vertexs = {}

def CONNECT(u, v):
    return Edge(u, v)

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.value in G.vertexs:
            G.vertexs[e.fromV.value] = EdgeList(e.fromV)
        if not e.toV.value in G.vertexs:
            G.vertexs[e.toV.value] = EdgeList(e.toV)

        G.vertexs[e.fromV.value].connectedV.append(e.toV)
        #G.vertexs[e.toV.value].connectedV.append(e.fromV)

def PRINTLIST(el):
    print(el.vertex.value, ":")
    for v in el.connectedV:
        print(v.value)

def PRINTGRAPH(G):
    for v in G.vertexs:
        PRINTLIST(G.vertexs[v])
        print("-----")

global time
time = 0
def DFS(G, TG, keys):
    for u in keys:
        G.vertexs[u].vertex.color = WHITE
        G.vertexs[u].vertex.pi = None
    global time
    time = 0
    k = 1
    for u in sorted(G.vertexs.keys()):
        if G.vertexs[u].vertex.color == WHITE:
            DFS_VISIT(G, G.vertexs[u].vertex, TG, k)
            k = k + 1

def DFS_VISIT(G, u, TG, k):
    global time
    time = time + 1
    u.d = time
    u.color = GRAY
    for v in G.vertexs[u.value].connectedV:
        if v.color == WHITE:
            v.pi = u
            DFS_VISIT(G, v, TG, k)
    u.color = BLACK
    time = time + 1
    u.f = time
    u.k = k
    TG.append(u)
    #print(u.value, u.d, u.f)

def TOPOLOGICAL_SORT(G, keys):
    TSG = []
    DFS(G, TSG, keys)
    return TSG

def TRANSPOSE(G):
    edges = []
    for k in G.vertexs.keys():
        u = G.vertexs[k].vertex
        for v in G.vertexs[u.value].connectedV:
            edges.append(CONNECT(v, u))

    GT = Graph()
    INITGRAPH(GT, edges)
    return GT

def STRONGLY_CONNECTED_COMPONENTS(G, k):
    GT = TRANSPOSE(G)

    tsg = TOPOLOGICAL_SORT(G, k)
    keys = []
    for u in tsg:
        keys.insert(0,u.value)
        print(u.value)

    gtts = []
    DFS(GT, gtts, keys)
    print("------------")
    k = 0
    for u in gtts:
        if u.k != k:
            print("|")
            k = u.k
        print(u.value)

if __name__ == "__main__":
    a = Vertex('a')
    b = Vertex('b')
    c = Vertex('c')
    d = Vertex('d')
    e = Vertex('e')
    f = Vertex('f')
    g = Vertex('g')
    h = Vertex('h')

    edges = []
    edges.append(CONNECT(a, b))
    edges.append(CONNECT(b, c))
    edges.append(CONNECT(b, e))
    edges.append(CONNECT(b, f))
    edges.append(CONNECT(c, d))
    edges.append(CONNECT(c, g))
    edges.append(CONNECT(d, c))
    edges.append(CONNECT(d, h))
    
    edges.append(CONNECT(e, a))
    edges.append(CONNECT(e, f))
    edges.append(CONNECT(f, g))
    edges.append(CONNECT(g, f))
    edges.append(CONNECT(g, h))
    edges.append(CONNECT(h, h))



    G = Graph()
    INITGRAPH(G, edges)
    PRINTGRAPH(G)

    #STRONGLY_CONNECTED_COMPONENTS(G, ['c', 'g','f','h','d','b','e','a'])
    keys = sorted(G.vertexs.keys())
    print(keys)
    STRONGLY_CONNECTED_COMPONENTS(G, keys)



习题解答

目录(Table of Contents)   前言(Preface)   第一部分(Part I) 基础(Foundations)   第一章 计算中算法的角色(The Role of Algorithms in Computing)   第二章 开始(Getting Started)   第三章 函数的增长率(Growth of Functions)   第四章 递归(Recurrences)   第五章 概率分析与随机化算法(Probabilistic Analysis and Randomized Algorithms)   第二部分(Part II) 排序与顺序统计(Sorting and Order Statistics)   第六章 堆排序(Heapsort)   第七章 快速排序(Quicksort)   第八章 线性时间中的排序(Sorting in Linear Time)   第九章 中值与顺序统计(Medians and Order Statistics)   第三部分(Part III) 数据结构(Data Structures)   第十章 基本的数据结构(Elementary Data Structures)   第十一章 散列表(Hash Tables)   第十二章 二叉查找树(Binary Search Trees)   第十三章 红-黑树(Red-Black Trees)   第十四章 扩充的数据结构(Augmenting Data Structures)   第四部分(Part IV) 高级的设计与分析技术(Advanced Design and Analysis Techniques)   第十五章 动态规划(Dynamic Programming)   第十六章 贪婪算法(Greedy Algorithms)   第十七章 分摊分析(Amortized Analysis)   第五部分(Part V) 高级的数据结构(Advanced Data Structures)   第十八章 B-树(B-Trees)   第十九章 二项式堆(Binomial Heaps)   第二十章 斐波纳契堆(Fibonacci Heaps)   第二十一章 不相交集的数据结构(Data Structures for Disjoint Sets)   第六部分(Part VI) 算法(Graph Algorithms)   第二十二章 基本算法(Elementary Graph Algorithms)   第二十三章 最小生成树(Minimum Spanning Trees)   第二十四章 单源最短路径(Single-Source Shortest Paths)   第二十五章 全对的最短路径(All-Pairs Shortest Paths)   第二十六章 最大流(Maximum Flow)   第七部分(Part VII) 精选的主题(Selected Topics)   第二十七章 排序网络(Sorting Networks)   第二十八章 矩阵运算(Matrix Operations)   第二十九章 线性规划(Linear Programming)   第三十章 多项式与快速傅里叶变换(Polynomials and the FFT)   第三十一章 数论算法(Number-Theoretic Algorithms)   第三十二章 字符串匹配(String Matching) ......................................................
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值