Chapter 23 "Minimum Spanning Tree": Kruskal and Prim algorithm, python implementation

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 ETE , 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:
Insert picture description here
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:
Insert picture description here
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
  1. Sort all edges from small to large according to their weights
  2. 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
  3. 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
Insert picture description here

Implementation steps
  1. Randomly select a node to initialize the minimum tree
  2. For all the edges connecting the tree and the new node, select the edge with the smallest weight
  3. 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 )

Guess you like

Origin blog.csdn.net/BGoodHabit/article/details/106895320