最小生成树问题之Kruskal算法

算法步骤

在这里插入图片描述
注:当然前提还是得保证图是无向连通的图。

Python代码

import numpy as np
from numpy import inf

def kruskal(D):
    # 因为在无向图中不想考虑对角元,将对角元化为无穷大。
    t1, t2 = np.where(D == 0)
    D[t1, t2] = inf

    T = np.zeros([D.shape[0], D.shape[1]])  # 初始化最终的最小生成树带权矩阵
    V = []  # 初始化最小生成树顶点集

    # 算法迭代主体
    while not (len(V) == 1 and len(V[0]) == D.shape[0]):
        t1, t2 = np.where(D == D.min())
        if len(V) == 0:
            V.append([t1[0], t2[0]])
        else:
            # 找到现考虑的边的两个端点有无包含在已有子树内和在哪棵子树内
            pos_1 = [pos for pos, value in enumerate(V) if t1[0] in value]
            pos_2 = [pos for pos, value in enumerate(V) if t2[0] in value]

            # 新的带权边不含于现存任何一棵子树内
            if len(pos_1) == 0 and len(pos_2) == 0:
                V.append([t1[0], t2[0]])
            else:

                # 新的带权边的一个顶点在某棵子树内, 另一个顶点不在任一棵现存子树内
                if len(pos_1) == 0 and len(pos_2) == 1:
                    V[pos_2[0]].append(t1[0])
                elif len(pos_1) == 1 and len(pos_2) == 0:
                    V[pos_1[0]].append(t2[0])

                # 新的带权边的两个顶点在两棵不同子树内
                elif pos_1[0] != pos_2[0]:
                    t = V[pos_1[0]] + V[pos_2[0]]
                    V = [value for pos, value in enumerate(V) if (pos != pos_1[0] and pos != pos_2[0])]
                    V.append(t)

                # 新的带权边的两个顶点在同棵子树内
                else:
                    D[t1[0], t2[0]] = D[t2[0], t1[0]] = inf
                    continue

        T[t1[0], t2[0]] = D[t1[0], t2[0]]
        D[t1[0], t2[0]] = D[t2[0], t1[0]] = inf

    print(T)

Matlab代码

%% 输入
% % 方式一:输入距离矩阵
% D = [
%     ];  % 在这里输入带权邻接矩阵
% % 计算图的顶点数
% [N,~] = size(D);
% % 生成D的边权数据edge,是一个n*3的矩阵,其中第1,2列是边两端顶点的编号,第3列是边权
% edge = [];
% for i = 1:(N-1)
%     for j = (i+1):N
%         edge = [edge;[i,j,D(i,j)]];
%     end
% end

% 方式二:直接输入n*3的边权矩阵
edge = [
    ];

%% 整理,初始化数据
% 对生成的edge按第3列边权从上向下递增的顺序整理edge
edge = sortrows(edge,3);

NodeNum=max(max(edge(:,1:2)));  % 网络顶点数
e1 = length(edge(:,1));  % 网络边数

p = 1;  % p是当前连通子树的编号
TreeNode = [edge(1,1), p; edge(1,2), p];  % 当前子树的顶点集
TreeEdge = [edge(1,:)];  % 当前子树的边集

%% 算法迭代主体
for i=2:e1
    if length(TreeEdge(:,1)) < (NodeNum-1)  % 测试将当前边加入子树是否会形成圈
        k1 = 0; k2 = 0;
        for j = 1:length(TreeNode(:,1))
            if edge(i,1) == TreeNode(j,1)
                k1 = 1; p1 = TreeNode(j,2);
            end
        end
        for j = 1:length(TreeNode)
            if edge(i,2) == TreeNode(j,1)
                k2 = 1; p2 = TreeNode(j,2);
            end
        end
        
        % 当前边的2个顶点都不在已形成的子树中
        if k1 + k2 == 0
            TreeEdge = [TreeEdge ; edge(i,:)];
            p = max(TreeNode(:,2)) + 1;
            TreeNode = [TreeNode;edge(i,1),p;edge(i,2),p ];
        end
        
        % 当前边的其中一个顶点不在已形成的子树中
        if k1 + k2 == 1
            TreeEdge = [TreeEdge;edge(i,:)];
            if k1 == 1
                p = p1;
                TreeNode = [TreeNode;edge(i,2),p];
            else
                p = p2;
                TreeNode = [TreeNode;edge(i,1),p];
            end
        end
        
        % 当前边的2个顶点分别属于已形成的不同的连通子树中
        if k1 + k2 == 2 && p1 ~= p2
            TreeEdge = [TreeEdge;edge(i,:)];
            if p1 < p2
                t = find(TreeNode(:,2) == p2);
                TreeNode(t,2) = p1;
            else
                t = find(TreeNode(:,2) == p1);
                TreeNode(t,2) = p2;
            end
        end
    end
end
TreeEdge

例题(无向图)

运行下列代码生成无向图:

import networkx as nx
import matplotlib.pyplot as plt

G = nx.Graph()
G.add_weighted_edges_from([('A', 'S', 2), ('A', 'B', 2), ('A', 'D', 7),
                           ('S', 'B', 5), ('B', 'D', 5), ('D', 'T', 5),
                           ('S', 'C', 4), ('B', 'C', 1), ('B', 'E', 3),
                           ('D', 'E', 1), ('T', 'E', 7), ('C', 'E', 4)])
edge_labels = dict([((u, v), d['weight']) for u, v, d in G.edges(data=True)])
pos = nx.spring_layout(G)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=15)
nx.draw_networkx(G, pos, node_size=400)
plt.show()

题目示意图:
在这里插入图片描述

其中各点代表村镇,它们之间的连线表明各村镇之间能否直接交通来往,连线旁边的数字代表道路的长度。现要求沿图中道路架设电线,使得上述村镇全部通上电,应该如何架设使得总的线路长度为最短(即求出最小生成树)。

Python代码求解

# 输入初始的带权邻接矩阵
D = np.array([[0, 2, inf, 7, inf, inf, 2],
              [2, 0, 1, 5, 3, inf, 5],
              [inf, 1, 0, inf, 4, inf, 4],
              [7, 5, inf, 0, 1, 5, inf],
              [inf, 3, 4, 1, 0, 7, inf],
              [inf, inf, inf, 5, 7, 0, inf],
              [2, 5, 4, inf, inf, inf, 0]])
kruskal(D)

  结果:

[[0. 2. 0. 0. 0. 0. 2.]
 [0. 0. 1. 0. 3. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1. 5. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0.]]

其中非零项对应的边即为树枝。

Matlab代码求解

% 采用方式2的输入, 数据如下
edge = [
    1 2 2
    1 4 7
    1 7 2
    2 3 1
    2 4 5
    2 5 3
    2 7 5
    3 5 4
    3 7 4
    4 5 1
    4 6 5
    5 6 7
    ];

  结果:

TreeEdge =
     2     3     1
     4     5     1
     1     2     2
     1     7     2
     2     5     3
     4     6     5

拓:Python-networkx包求解

T = nx.minimum_spanning_tree(G)  # G来源于题目一开始生成无向图的代码
print(sorted(T.edges(data=True)))

  结果:

[('A', 'B', {'weight': 2}), ('A', 'S', {'weight': 2}), 
('B', 'C', {'weight': 1}), ('B', 'E', {'weight': 3}), 
('D', 'E', {'weight': 1}), ('D', 'T', {'weight': 5})]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

此账号已停更

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值