Python实现最小生成树(Prim算法)

前言

使用链表构建图,利用递归、栈实现Prim算法求取最小生成树。
或许实现的结果与最小生成树的定义有区别,下面代码的结果是:穷举所有的节点,获取从任何顶点出发的一条最小权重的路径。
代码:for v in g.vertices(): 是从所有顶点出发,如果需要从指定顶点出发可以修改为,如设置从a出发:v = g.get_vertex(“a”)。将这行代码替换for 循环即可。(或许这里无法理解,当你对照着代码可能会理解)
此代码是博主自己实现,可能存在一些找不全、甚至找不到结果的极端情况,如有错误敬请指正。

文件集合:
在这里插入图片描述

完整代码见GitHub:

https://github.com/GYT0313/Python-DataStructure/tree/master/10-graph/example-TestGraph

1. 运行示例

图结构:
在这里插入图片描述
代码界面:
在这里插入图片描述
图的实现可以从键盘输入 or 从文件读取:

  • 从键盘输入
    在这里插入图片描述
  • 从文件读取
    在这里插入图片描述
    在这里插入图片描述
    查看图:
    在这里插入图片描述
    生成最小生成树:
    在这里插入图片描述

2. Prim代码

完整运行代码见GitHub:
https://github.com/GYT0313/Python-DataStructure/tree/master/10-graph/example-TestGraph
Prim代码(algorithms.py):

def span_tree(g, start_label):
    """最小生成树"""
    # sys.setrecursionlimit(1500)  # set the maximum depth as 1500
    min_weights = 99999
    results = []

    def recurse(w, stack):
        if w.is_marked() == False:
            # 顶点未标记则入栈
            stack.push(w)
            w.set_mark()
            # 未遍历完的情况
            if stack.get_size() != g.size_vertices():
                for x in w.neighboring_vertices():
                    # 继续从顶点的连通节点往下继续遍历
                    recurse(x, stack)
                    # 如果从w的连通节点再往下没有连通节点,
                    # 则出栈w已入栈的连通节点
                    if stack.peek() != w and \
                            stack.get_size() < g.size_vertices():
                        temp = stack.pop()
                        temp.clear_mark()
            else:
                # 已经遍历完所有顶点,并且满足了条件,
                # 但最后一个节点还有连通的顶点时,不再遍历
                # if w.get_edge_count() > 0:
                #     return None
                pass
        else:
            return None

    def generate_results(sun_stack):
        temp = ""
        for v_temp in sun_stack:
            temp += v_temp.get_label() + "->"
        temp = temp[:len(temp) - 2]
        temp += ": " + str(sun_stack.get_weights())
        temp += "\n"
        results.append(temp)

    for v in g.vertices():
        # stack存储整个顶点, sun_stack复制stack的最新状态并继续遍历
        stack = LinkedStack()
        stack.push(v)
        sun_stack = copy.deepcopy(stack)
        for w in v.neighboring_vertices():
            # 清除所有顶点的标记状态(因为上一次的遍历顶点已经被标记)
            g.clear_vertex_marks()
            v.set_mark()
            # 递归遍历
            recurse(w, sun_stack)
            # 如果遍历结束,栈内顶点数==图的顶点数代表该次遍历成功,加入成功列表
            if sun_stack.get_size() == g.size_vertices():
                if sun_stack.get_weights() < min_weights:
                    results.clear()
                    generate_results(sun_stack)
                    min_weights = sun_stack.get_weights()
                elif sun_stack.get_weights() == min_weights:
                    generate_results(sun_stack)
            else:
                # 未找到路径则继续下一个连接顶点的遍历
                pass
            sun_stack = copy.deepcopy(stack)

    if results == None:
        return "No best path"
    string = "Here are " + str(len(results)) + " path:\n"
    results.insert(0, string)
    return results

3. 另一个示例

这个示例用2018年五一数学建模A题为例。
问题1.从景石(a)出发,步行游览以下景点: ①游客服务中心,②阳光草坪,③森林小剧场,④儿童科普体验区,⑤儿童戏水场,⑥湿地博物馆,⑦湿地商业街。建立数学模型,找出从景石出发,到达⑦湿地商业街,并且经过①—⑥所有景点至少1次的距离最短的路线,计算该路线的长度,并将相关结果填入表格3。注:在每个景点不用停留。
对其改编,从任何节点出发,并且不能重复。(符合代码,不然也可以更改代码来符合题意)
原数据:
在这里插入图片描述
在这里插入图片描述
准备数据:
文件:51.txt

a->b:300 a->c:360 a->d:210 a->e:590 a->f:475 a->g:500 a->h:690
b->a:300 b->c:380 b->d:270 b->e:230 b->f:285 b->g:200 b->h:390
c->a:360 c->b:380 c->d:510 c->e:230 c->f:765 c->g:580 c->h:770
d->a:210 d->b:270 d->c:510 d->e:470 d->f:265 d->g:450 d->h:640
e->a:590 e->b:230 e->c:230 e->d:470 e->f:515 e->g:260 e->h:450
f->a:475 f->b:285 f->c:765 f->d:265 f->e:515 f->g:460 f->h:650
g->a:500 g->b:200 g->c:580 g->d:450 g->e:260 g->f:460 g->h:190
h->a:690 h->b:390 h->c:760 h->d:640 h->e:450 h->f:650 h->g:190

生成图:
在这里插入图片描述

运行:
在这里插入图片描述

1835 = 265 + 210 + 300 + 380 + 230 +260 + 190

如果有错误或优化请指正。

完!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值