算法导论 第二十三章 最小生成树

def:对于图G(V,E)集合E中的边带有权重,存在一个包含所有节点的树并树边为E的子集,使得所有边的权重最小,则该树称为最小生成树。

32.1 最小生成树的形成

我们使用贪心算法来找到一个最小生成树

算法导论 第二十三章 最小生成树_第1张图片

算法导论 第二十三章 最小生成树_第2张图片

算法导论 第二十三章 最小生成树_第3张图片

算法导论 第二十三章 最小生成树_第4张图片

算法导论 第二十三章 最小生成树_第5张图片

算法导论 第二十三章 最小生成树_第6张图片

23.2 Kruskal算法和Prim算法

两个算法一个直接对边权重大小进行排序,一步步加入到树中;另一个则是根据跨越分割的边(既从树节点到未加入树的节点的边)的权重来选择节点。示意图如下:

算法导论 第二十三章 最小生成树_第7张图片

算法导论 第二十三章 最小生成树_第8张图片

算法导论 第二十三章 最小生成树_第9张图片

算法导论 第二十三章 最小生成树_第10张图片

实现代码如下:

class Node():  
    def __init__(self, x):  
        self.value = x  
        self.p = self  
        self.rank = 0  
  
def MAKE_SET(x):  
    x.p = x  
    x.rank = 0  
  
def UNION(x, y):  
    LINK(FIND_SET(x), FIND_SET(y))  
  
def LINK(x, y):  
    if x.rank > y.rank:  
        y.p = x  
    else:  
        x.p = y  
        if x.rank == y.rank:  
            y.rank = y.rank + 1  
  
def FIND_SET(x):  
    if x != x.p:  
        x.p = FIND_SET(x.p)  
    return x.p

##=================================##
def PARENT(i):
    return (i-1) // 2

def LEFT(i):
    return i*2 + 1

def RIGHT(i):
    return i*2 + 2

def MIN_HEAPIFY(A, i, size):
    l = LEFT(i)
    r = RIGHT(i)
    
    if l < size and A[l] < A[i]:
        largest = l
    else:
        largest = i
    if r < size and A[r] < A[largest]:
        largest = r

    if largest != i:
        temp = A[i]
        A[i] = A[largest]
        A[largest] = temp
        MIN_HEAPIFY(A, largest, size)
    
    
def BUILD_MIN_HEAP(A):
    size = len(A)
    for i in range(len(A)//2, -1, -1):
        MIN_HEAPIFY(A, i, size)

def HEAP_MINIMUM(A):
    return A[0]

def HEAP_EXTRACT_MIN(A, size):
    assert(size > 0)
    iMIN = A[0]
    size = size - 1
    A[0] = A[size]
    MIN_HEAPIFY(A, 0, size)
    return iMIN

def HEAP_DECREASE_KEY(A, i, key):
    assert(A[i].key > key)
    A[i].key = key
    while i > 0 and PARENT(i) >= 0 and A[PARENT(i)] > A[i]:
        temp = A[i]
        A[i] = A[PARENT(i)]
        A[PARENT(i)] = temp
        i = PARENT(i)

##====================###

class Vertex:  
    def __init__(self, u):  
        self.value = u
        self.vertexs = []
        self.isInGraph = False
        self.set = None
        self.key = float("inf")
        self.pi = None
    def __lt__(self, other):
        return self.key <= other.key
  
class Edge:  
    def __init__(self, u, v, w):  
        self.fromV = u  
        self.toV = v
        self.weight = w

class Graph:
    def __init__(self):  
        self.vertexs = []
        self.edges = []

def weight(edges, u, v):
    for e in edges:
        if (e.fromV == u and e.toV == v) or (e.fromV == v and e.toV == u):
            return e.weight
    
    return None
def isInHeap(Q, u, v):
    if u.key == v.key:
        return v == Q[0]
    return u.key < v.key

def INITGRAPH(G, edges):
    for e in edges:
        if not e.fromV.isInGraph:
            G.vertexs.append(e.fromV)
            e.fromV.isInGraph = True
        if not e.toV.isInGraph:
            G.vertexs.append(e.toV)
            e.toV.isInGraph = True
        
        G.edges.append(e)
        e.fromV.vertexs.append(e.toV)
        e.toV.vertexs.append(e.fromV)

def MST_KRUSKAL(G):
    A = []
    for v in G.vertexs:
        s = Node(v)
        MAKE_SET(s)
        v.set = s
    edges = sorted(G.edges, key=lambda edge : edge.weight)
    for edge in edges:
        if FIND_SET(edge.fromV.set) != FIND_SET(edge.toV.set):
            A.append(edge)
            UNION(edge.fromV.set, edge.toV.set)
    return A

    
def MST_PRIM(G, r):
    Q = []
    for u in G.vertexs:
        u.key = float("inf")
        u.pi = None
        Q.append(u)
    
    r.key = 0
    BUILD_MIN_HEAP(Q)
    size = len(Q)
    while size != 0:
        u = HEAP_EXTRACT_MIN(Q, size)
        print(u.value)
        size = size - 1
        for v in u.vertexs:
            if v in Q and Q.index(v) < size and weight(G.edges, u, v) < v.key:
                v.pi = u
                HEAP_DECREASE_KEY(Q, Q.index(v), weight(G.edges, u, v))

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(Edge(a, b, 4))
    edges.append(Edge(a, h, 8))
    edges.append(Edge(b, h, 11))
    edges.append(Edge(b, c, 8))
    edges.append(Edge(c, d, 7))
    edges.append(Edge(c, f, 4))
    edges.append(Edge(c, i, 2))
    edges.append(Edge(d, e, 9))
    edges.append(Edge(d, f, 14))
    edges.append(Edge(e, f, 10))
    edges.append(Edge(f, g, 2))
    edges.append(Edge(g, i, 6))
    edges.append(Edge(g, h, 1))
    edges.append(Edge(h, i, 7))

    G = Graph()  
    INITGRAPH(G, edges)
    A = MST_KRUSKAL(G)
    for edge in A:
        print(edge.fromV.value, edge.toV.value, edge.weight)

    print("*****************")
    MST_PRIM(G, a)


    

两个函数的复杂度都为O(ElgV)

note:prim算法两层循环执行的次数总共为2|E|(只有互相连接的边才会遍历,来回各一次),HEAP_DECREASE_KEY花费lgv。

算法导论 第二十三章 最小生成树_第11张图片


习题解答


你可能感兴趣的:(算法笔记)