算法-图论-最小生成树

0. Prim和Kruskal算法的区别

基本思想 时间复杂度 使用场景
Prim 将顶点逐个加入集合,维护minDist[],贪心选择距离集合最近的顶点加入。 \(O(V^2)\) 运行效率与节点数相关,与边数无关。适合稠密图
Kruskal 先对边进行排序;再选择连接不同分量的最短边(使用并查集判断是否处于不同分量)。 \(O(ElogE)\) 运行效率只与边数相关。适合稀疏图

1. Prim算法(卡码网 53)

# 最小生成树prim算法 
# 贪心地选择距离已有集合最小的边加入
def prim(graph) -> int:
    result = 0
    visited = [False for _ in range(len(graph))]
    # 未选点距离已选集合的最短距离
    minDist = [10001 for _ in range(len(graph))]
    
    # 初始化:加入节点1
    visited[1] = True
    for i in range(2, len(minDist)):
        minDist[i] = graph[1][i]
    
    # 还要加入n-1个顶点
    for i in range(len(minDist)-2):
        node_to_add = -1
        minVal = 10001
        for j in range(2, len(minDist)):
            if visited[j] == False and minDist[j] < minVal:
                minVal = minDist[j]
                node_to_add = j
        
        # 将最近的未访问的节点加入集合
        visited[node_to_add] = True
        result += minVal
        
        # 更新minDist数组
        for j in range(2, len(minDist)):
            if visited[j] == False and graph[node_to_add][j] < minDist[j]:
                minDist[j] = graph[node_to_add][j]
    
    return result


def main():
    num_node, num_edge = map(int, input().split())
    graph = [[10001 for _ in range(num_node+1)] for _ in range(num_node+1)]
    for _ in range(num_edge):
        node1, node2, val = map(int, input().split())
        graph[node1][node2] = val
        graph[node2][node1] = val
    
    
    print(prim(graph))
    
    
if __name__ == "__main__":
    main()

2. Kruskal算法(卡码网 53)

class Edge:
    def __init__(self, node1, node2, val):
        self.node1 = node1
        self.node2 = node2
        self.val = val

# 并查集
class UnionFindSet:
    # 初始化并查集
    def __init__(self, n):
        self.father = list(range(n))
    
    # 寻找本联通分量的根
    def find(self, node1:int) -> int:
        if node1 != self.father[node1]:
            self.father[node1] = self.find(self.father[node1])
        return self.father[node1]

    def join(self, node1: int, node2: int):
        root1 = self.find(node1)
        root2 = self.find(node2)
        if root1 != root2:
            self.father[root2] = root1
    
    def isSame(self, node1: int, node2: int):
        return self.find(node1) == self.find(node2)

# 最小生成树 kruskal算法 选择连接不同分量的最短边
def kruskal(num_node, edges) -> int :
    ufs = UnionFindSet(num_node + 1)
    # 根据edge.val进行排序
    edges.sort(key=lambda edge: edge.val)
    
    result = 0
    for e in edges:
        if ufs.isSame(e.node1, e.node2) == False:
            ufs.join(e.node1, e.node2)
            result += e.val
    
    return result

def main():
    num_node, num_edge = map(int, input().split())
    edges = []
    for _ in range(num_edge):
        node1, node2, val = map(int, input().split())
        edges.append(Edge(node1, node2, val))
    
    print(kruskal(num_node, edges))
    
    
if __name__ == "__main__":
    main()
posted @ 2024-11-02 11:37  Frank23  阅读(9)  评论(0编辑  收藏  举报