最小生成树算法-- K算法、P算法

本文介绍了图论中的最小生成树问题,重点讲解了两种经典算法——Kruskal算法和Prim算法。Kruskal算法利用小根堆和并查集避免形成环,Prim算法则通过不断加入最小边来构建最小生成树。这两种算法都是求解图中连接所有节点的最小成本路径,对于网络优化和资源分配等问题有广泛应用。
摘要由CSDN通过智能技术生成

最小生成树

最小生成树 (Minimum Spanning Tree) ,假设一个图中有N个节点,用N-1条边将所有N个节点连通起来,要求这N-1条边的权值之和最小。常用的算法有:Kruskal(克鲁斯卡尔)算法和Prim(普里姆)算法。

Kruskal 算法

Kruskal 算法利用小根堆和并查集来实现,步骤:

  1. 总是从权值最小的边开始考虑,依次考察权值依次变大的边
  2. 当前的边要么进入最小生成树的集合,要么丢弃
  3. 如果当前的边进入最小生成树的集合中,不会形成环,就要当前边,否则不要
  4. 考察完所有边之后,最小生成树的集合就得到了
class UnionFind:
    """
    并查集基础算法
    """
    def __init__(self):
        self.parents = dict()
        self.size_map = dict()

    def makeSets(self, nodes: List[Node]):
        self.parents.clear()
        self.size_map.clear()

        for node in nodes:
            self.parents[node] = node
            self.size_map[node] = 1

    def findFather(self, cur: Node):
        """
        给你一个节点,请你往上到不能再往上,把代表节点返回
        :param cur:
        :return:
        """
        path_stack = []
        while cur != self.parents.get(cur):
            path_stack.append(cur)
            cur = self.parents.get(cur)

        while path_stack:
            self.parents[path_stack.pop()] = cur

        return cur

    def isSameSet(self, a, b):
        return self.findFather(a) == self.findFather(b)

    def union(self, a, b):
        a_head = self.findFather(a)
        b_head = self.findFather(b)

        if a_head != b_head:
            a_set_size = self.size_map.get(a_head)
            b_set_size = self.size_map.get(b_head)
            big = a_head if a_set_size >= b_set_size else b_head
            small = b_head if big == a_head else a_head
            self.parents[small] = big
            self.size_map[big] = a_set_size + b_set_size
            self.size_map.pop(small)

        return self.size_map.__len__()

    def getSetNum(self):
        return len(self.size_map)


class Kruskal:

    def kruskalMST(self, graph: Graph):
        unionfind = UnionFind()
        unionfind.makeSets(graph.nodes.values())

        # 从小的边到大的边,依次弹出,小根堆!
        hp = []
        for edge in graph.edges:
            heapq.heappush(hp, edge)

        res = set()
        while hp:
            edge = heapq.heappop(hp)
            if not unionfind.isSameSet(edge.src, edge.dst):
                res.add(edge)
                unionfind.union(edge.src, edge.dst)

        return res

Prim 算法

Prim算法使用两个容器:小根堆和集合。
小根堆用来存放所有被解锁的边,集合用来保存所有被解锁过的点。
每个边有四种状态:锁定、被解锁、选中、丢弃。

步骤:点->所有相邻的边,左右两端有不在点集之中的点的边中取最小权重->点->repeat

class Prim:
    def primMst(self, graph: Graph):
        # 解锁的边进入小根堆
        heap_edge = list()
        # 哪些点被解锁出来了
        nodeSet = set()
        # 被选中的边放在结果集合中
        result = set()

        for node in graph.nodes.values():  # 遍历每个点是避免有多个连通子图
            if node not in nodeSet:
                nodeSet.add(node)
                for edge in node.edges:
                    heapq.heappush(heap_edge, edge)

                while heap_edge:
                    edge = heapq.heappop(heap_edge)
                    dst = edge.dst
                    if dst not in nodeSet:
                    	result.add(edge)
                        nodeSet.add(dst)
                        for nextEdge in dst.edges:
                            heapq.heappush(heap_edge, nextEdge)
		return result
		
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NLP_wendi

谢谢您的支持。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值