Minimum spanning tree
Suppose a connected undirected graph G = (V, E) G = (V, E)G=( V ,E ) , where each edge(u, v) ∈ E (u,v) \in E( u ,v )∈E , we give it weightw (u, v) w(u,v)w(u,v ) , we hope to find an acyclic subsetT ⊆ ET \subseteq ET⊆E , can connect all the nodes, but also has the smallest weight. That is,w (T) = ∑ (u, v) ∈ T w (u. V) w(T)=\sum_((u,v) \in T)w(uv)w(T)=∑( U , v ) ∈ TThe value of w ( u . v ) is the smallest. Thanks toTTT is acyclic and connects all nodes. Therefore,TTT must be a tree. We call such a tree aspanning tree, and we call the problem of obtaining the spanning tree a minimum spanning tree problem. The following figure describes an example of a connected graph and its minimum spanning tree:
In the graph, the edges belonging to the minimum spanning tree are shaded, and the total weight of the spanning tree shown in the figure is 37. However, the minimum spanning tree Is not unique, delete the edge(b, c) (b, c)(b,c ) , then add edge(a, h) (a, h)(a,h ) , will form another minimum spanning tree with a weight of 37.
Two algorithms to solve the minimum spanning tree problem: Kruskal algorithm and Prim algorithm. Both minimum spanning tree algorithms are greedy algorithms.
Kruskal algorithm and Prim algorithm
Two classic algorithms for the minimum spanning tree problem, in the Kruskal algorithm, set AAA is a forest, and its nodes are the nodes of a given graph, each time it is added to the setAAThe safe edge in A is always the edge connecting two different components with the smallest weight. In the Prim algorithm, setAAA is a tree, every time it is added toAAThe safe side in A is always connected toAAA sumAAThe edge with the smallest weight among the edges of a node other than A.
Kruskal algorithm
The Kruskal algorithm finds the safe edge by finding the edge with the smallest weight (u, v) (u, v) among all the edges connecting two different trees in the forest( u ,v ) , Kruskal algorithm is a greedy algorithm, because it chooses an edge with the smallest weight to join the forest every time. The following figure shows the working process of Kruskal's algorithm:
the shadowed edge belongs to the growing forestAAA , the algorithm considers the weights of the edges in turn, and the edges pointed to by the arrows are the edges examined at each step of the algorithm. If the edge connects two different trees, it is added to the forest, thus completing the merging of the two trees.
Realization ideas
Input : graph
output : minimum tree
minimum tree satisfies the condition :
- Contains all nodes
- Among all the trees formed by the graph, the tree with the smallest total score
Implementation steps
- Sort all edges from small to large according to their weights
- Each time the smallest edge is selected and added to the tree, if the newly added edge causes a ring in the tree, the edge is discarded
- Repeat the operation of adding edges in 2 above until the tree contains all the nodes
The python implementation code is as follows:
# -*-coding:utf8 -*-
import sys
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = []
def add_edge(self, u, v, w):
self.graph.append([u,v,w])
# 找到节点所在的树的根节点
def find(self, parent, i):
if parent[i] == i:
return i
return self.find(parent, parent[i])
# 合并新的节点到一棵树中来
def apply_union(self, parent, rank, x, y):
xroot = self.find(parent, x)
yroot = self.find(parent, y)
if rank[xroot] < rank[yroot]:
parent[xroot] = yroot
elif rank[xroot] > rank[yroot]:
parent[yroot] = xroot
else:
parent[yroot] = xroot
rank[xroot] +=1
def kruskal(self):
result = []
i, e = 0, 0
#排序,边按照权重从小到大排序
self.graph = sorted(self.graph, key=lambda item: item[2])
parent = []
rank = []
#初始,每个节点构成一棵树,根节点就是自己
for node in range(self.V):
parent.append(node)
rank.append(0)
# 做V-1个节点选择
while e < self.V - 1:
u, v, w = self.graph[i]
i = i+1
x = self.find(parent, u)
y = self.find(parent, v)
# 选择的边的两个节点不在同一棵树,则合并
if x != y:
e = e + 1
result.append([u, v, w])
self.apply_union(parent, rank, x, y)
#打印每一次选择的边
for u, v , weight in result:
print("%d - %d: %d" % (u, v, weight))
if __name__=='__main__':
g = Graph(6)
for x,y,w in [(0,1,4),(0,2,4),(1,2,2),(1,0,4),(2,0,4),(2,1,2),(2,3,3),(2,5,2),(2,4,4),(3,2,3),(3,4,3),(4,2,4),(4,3,3),(5,2,2),(5,4,3)]:
g.add_edge(x,y,w)
g.kruskal()
The time complexity of Kruskal's algorithm is O (E lg E) O(ElgE)O ( E l g E )
Prim algorithm
One property of Prim’s algorithm is set AAA side is always constitute a tree, any tree from a root node, has been brought up to coverVVAll nodes in V are so far. This strategy is also a greedy strategy, because the edge added at each step must be the edge that minimizes the increase in the total weight of the tree.
The process of executing the Prim algorithm as shown in the figure below, the initial node isaaa , the shaded edges and black nodes belong to the treeAAA。
Implementation steps
- Randomly select a node to initialize the minimum tree
- For all the edges connecting the tree and the new node, select the edge with the smallest weight
- Repeat the above 2 until all nodes are included
Python is implemented as follows:
# -*-coding:utf8 -*-
import sys
INF = 9999999
class Graph:
def __init__(self, V, G):
self.V = V
self.G = G
def prim(self):
selected = [0] * self.V
no_edge = 0
selected[0] = True
print("Edge : Weight")
#需要选择V-1个
while (no_edge < self.V - 1):
minimum = INF
x = 0
y = 0
# 遍历V个节点
for i in range(V):
#该节点选择了的
if selected[i]:
for j in range(self.V):
#选择的邻接节点没有选择的,且有边的
if ((not selected[j]) and self.G[i][j]):
if minimum > self.G[i][j]:
minimum = self.G[i][j]
x = i
y = j
print(str(x) + "-" + str(y) + ":" + str(self.G[x][y]))
selected[y] = True
no_edge += 1
if __name__=='__main__':
V = 5
G = [[0, 9, 75, 0, 0],
[9, 0, 95, 19, 42],
[75, 95, 0, 51, 66],
[0, 19, 51, 0, 31],
[0, 42, 66, 31, 0]]
graph = Graph(V,G)
graph.prim()
Prim's algorithm complexity is O (E log V) O (ElogV)O ( E l o g V )