18.python实现最小生成树-克鲁斯卡尔算法

克鲁斯卡尔算法

克鲁斯卡尔算法(kruskal)跟普里姆算法一样,目的都是求无向图的最小生成树。普里姆算法核心在于一个顶点接一个顶点的找出最短路径,克鲁斯卡算法在于将每一条边进行升序排序,然后通过边进行筛选从而组成最小生成树。

实现步骤

核心思想在于按权值从小到大排序选择n-1条边,并保证选择边不会构成回路。用到核心的数据结构并查集来判断是否构成回路。

  1. 将所有的边按权值大小升序排列
  2. 创建一个数组selectEdges存在选择出来的边
  3. 循环遍历已经排好序的边,如果该边不构成回路,则添加到selectEdges

并查集

并查集是一种树型或者链表的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

  1. 初始化数组为的值为索引本身,如果arr[n]=n 说明该点的根节点就是他本身
  2. 并查集核心方法找到一个节点的根节点find 和 union合并2个节点
  3. union合并方法的时候如果根节点不同,则将一个根节点挂在另一个根节点上集合
from functools import reduce


class Edge(object):
    """
    边 start起点 end终点 weight 权值
    """

    def __init__(self, start, end, weight):
        self.start = start
        self.end = end
        self.weight = weight

    def __str__(self):
        return '%s->%s weight=%d' % (self.start, self.end, self.weight)


class MiniTree(object):
    def __init__(self, vertex, weight):
        """
        最小生成树
        """
        self.vertex = vertex
        self.weight = weight

    def get_all_edges(self):
        all_edges = []
        for s in range(len(self.vertex)):
            for e in range(len(self.vertex)):
                if self.weight[s][e] != 10000:
                    all_edges.append(Edge(s, e, self.weight[s][e]))
        return all_edges

    def disjoin_set_find_root(self, parent_root, start):
        """
        使用并查集思想找到一个顶点的终点
        :return:
        """
        while parent_root[start] != start:
            return self.disjoin_set_find_root(parent_root, parent_root[start])
        return start

    def disjoin_set_union(self, parent_root, start, end):
        """
        合并
        :param parent_root:
        :param start:
        :param end:
        :return:
        """
        start_root = self.disjoin_set_find_root(parent_root, start)
        end_root = self.disjoin_set_find_root(parent_root, end)
        if start_root != end_root:
            parent_root[start_root] = end_root

    def create_min_tree_by_kruskal(self):
        """
        1. 将所有的边按权值大小升序排列
        2. 创建一个数组selectEdges存在选择出来的边
        3. 循环遍历已经排好序的边,如果该边不构成回路,则添加到selectEdges
        :return:
        """
        # 所有选择的边 并升序排列
        all_edges = self.get_all_edges()
        all_edges.sort(key=lambda edge: edge.weight)
        select_edges = []
        parent_root = [i for i in range(len(self.vertex))]
        while len(select_edges) < len(self.vertex) - 1:
            # 判断根已经选择了的边是否构成回路 使用到了并查集思想
            for edge in all_edges:
                start = edge.start
                end = edge.end
                # 核心判断是不是构成回路 不构成回路 加入选择边和
                if self.disjoin_set_find_root(parent_root, start) != self.disjoin_set_find_root(parent_root, end):
                    select_edges.append(edge)
                    all_edges.remove(edge)
                    self.disjoin_set_union(parent_root, start, end)
        return select_edges


if __name__ == '__main__':
    mini_tree = MiniTree(['A', 'B', 'C', 'D', 'E', 'F', 'G'],
                         [[10000, 5, 7, 10000, 10000, 10000, 2], [5, 10000, 10000, 9, 10000, 10000, 3],
                          [7, 10000, 10000, 10000, 8, 10000, 10000], [10000, 9, 10000, 10000, 10000, 4, 10000],
                          [10000, 10000, 8, 10000, 10000, 5, 4], [10000, 10000, 10000, 4, 5, 10000, 6],
                          [2, 3, 10000, 10000, 4, 6, 10000], ])
    min_tree_by_kruskal = mini_tree.create_min_tree_by_kruskal()
    print(reduce(lambda x, y: x + y, map(lambda x: x.weight, min_tree_by_kruskal)))
    for i in min_tree_by_kruskal:
        print('%s->%s weight=%d' % (mini_tree.vertex[i.start], mini_tree.vertex[i.end], i.weight))
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值