Written in the front:
Sometimes we need to connect all nodes in undirected graphs at a certain cost. This is the knowledge of spanning trees. This chapter explains how to connect all nodes at a minimum cost.
difination:
Spanning Tree:
A graph without cross-cutting edges and anti-ancestor edges is a tree.
For a tree to have roots, it must have a point of zero entry.
For a graph, delete some edges to make it a tree, which is the spanning tree of the graph.
This spanning tree must be loop-free
Minimum Spanning Tree:
The spanning tree of a graph is not necessarily unique. We often need to find the smallest spanning tree (i.e. the least cost of all vertex connections).
In a given undirected graph
G
=
(
V
,
E
)
,
G = (V, E),
G=(V,E),
(
u
,
v
)
(u, v)
(u,v) represents the edge (i.e., the edge) connecting the vertex u with the vertex v, and
w
(
u
,
v
)
w (u, v)
w(u,v) represents the weight of the edge. If there exists a subset (i.e., T) of E and it is an acyclic graph, so that
w
(
T
)
w (T)
w(T) is the smallest spanning tree of
G
G
G, then
T
T
T is the smallest spanning tree of
G
G
G:
w
(
T
)
=
∑
(
u
,
v
)
∈
T
w
(
u
,
v
)
w(T)=\sum_{(u,v)\in T}w(u,v)
w(T)=(u,v)∈T∑w(u,v)
It has some properties:
-
There are and only n − 1 n-1 n−1edges ( n n n is the node number)
-
The minimum outgoing edge of any node must belong to MST
-
The set of ordered edge weights of any two minimum spanning trees is the same
In the following literature, the corresponding proof will be given.
Graph Segmentation:
Segmentation definition:
Divide a graph into two connected sets, and record the partition of the graph.
Graph segmentation is not necessarily unique.
Segmentation theorem:
The minimum edge connecting two connected sets must belong to MST
Adequacy proof:
-
Assuming that the theorem is not valid, there is another cross-cutting edge belonging to the minimum spanning tree.
-
Adding the shortest transverse cutting edge is bound to form a loop.
-
By deleting the original cross-cutting edge, we can get a spanning tree.
-
Notice that the only difference between the two spanning trees is the two crosscutting edges.
-
Then the weight of the spanning tree constructed subsequently is larger than the minimum spanning tree defined by us, so it is contradictory.
That is to say, proving the validity of the segmentation theorem.
Overview of Algorithms:
There are three existing minimum spanning tree algorithms: Sollin, Kruskal and prim.
Pirm Algirithm:
The core of the algorithm-the explanation of ideas:
For the original graph G = ( V , E ) G= (V,E) G=(V,E), the nodes that have been added to the minimum spanning tree are put into the set V n e w V_{new} Vnew. According to the sharding theorem: the shortest edge connecting V n e w V_{new} Vnew and its complement set must belong to the minimum spanning tree, so we can expand according to this theorem until V n e w = V V_{new}=V Vnew=V
For the sake of convenience, we remember that the node adding the minimum spanning tree is the white point, the rest nodes are the blue points, and the shortest edge of the set V n e w V_{new} Vnewis the purple edge.
Algorithm flow:
-
Input a weighted connected graph, its edge set is E, node set is V
-
Initialize V n e w = { x } V_{new}=\{x\} Vnew={x}, x defaults to the root node of the spanning tree. E n e w = { } E_{new}=\{\} Enew={}, an empty set
-
Repeat until V n e w = V V_{new}=V Vnew=V
-
Find an edge ( x , y ) (x,y) (x,y), satisfying that it is the minimum edge of x x x, and x ∈ V n e w x\in V_{new} x∈Vnew, y ∉ V n e w y ∉ V_{new} y∈/Vnew
-
Add node y y y to the set of V n e w V_{new} Vnew
-
Add edge ( x , y ) (x,y) (x,y) to E n e w E_{new} Enew set
- At the end of the algorithm, the minimum spanning tree is:
MST = ∑ e ∈ E new w ( e ) \texttt{MST}=\sum_{e\in \texttt{E new}}w(e) MST=e∈E new∑w(e)
A heap/line segment tree can be used to maintain the current minimum outgoing edge
Coms the code:
void prim()
{
for(int i = 1; i <= n; i++)
dis[i] = inf;
dis[1] = 0;
Q.push((node){0, 1});
while(!Q.empty())
{
node x = Q.top();
Q.pop();
int Purple_edge = x.w;
int u = x.pos;
if(!vis[u])
{
vis[u] = 1;
MST += Purple_edge;
for(int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if(dis[v] > edge[i].w)
{
dis[v] = edge[i].w;
Q.push((node){dis[v], v});
}
}
}
}
}
Kruskal Algorithm:
Algorithm overview:
Commonly known as the additive method, it sorts all edge weights, examines each edge ( x , y ) (x, y) (x,y) in turn, and ensures that it connects two different connected components.
If it connects two different connected components, and we can be sure that it is the purple edge (which is obvious), then it can be proved that this edge must belong to the minimum spanning tree.
Algorithm flow:
-
Rank edge weights from smallest to largest and examine all edges in turn.
-
Initialization, V n e w , E n e w V_{new}, E_{new} Vnew,Enew as two empty sets.
-
If the edge connected two different connected components are considered, they are added to the edge set of the minimum spanning tree
-
When V n e w = V V_{new}=V Vnew=V, the algorithm ends
Pseudo code:
sort(E);
for: E->1 to n
{
if: E->(x,y) Connect two connected components
{
put E->(x,y) to E_new;
unionn(x, y);
}
}
Whether two nodes belong to the same connected component can be realized by the union search set.
Union-find set:
And look up this data structure can be convenient and fast to solve this problem. The basic idea is to initially treat each object as a collection of individual elements. Then the two elements in the connected edge are merged by reading the connected edge in turn. In this process, a search operation is repeated to determine a collection within that collection. When reading in a connected edge ( x , y ) (x, y) (x,y), first judge whether x x x and y y y are in the same set, if so, do not merge; If not, then use a merge operation to merge the sets of x x x and y y y, so that any two elements in the two sets are connected. Therefore, the search and merge operations are mainly used in the processing of the union search set.
In order to facilitate the description and implementation of the union, the elements added successively to a set are usually represented as a tree structure, and the serial number of the root node is used to represent the set. So, define an array of father [ n ] \texttt{father}[n] father[n], where father [ i ] \texttt{father}[i] father[i] is the number of the parent of node i i i in the tree where i i i is. For example, if father [ 4 ] = 5 \texttt{father}[4]=5 father[4]=5, the father of node 4 4 4 is node 5 5 5. Convention: if the parent of i i i (i.e., father[I]) is a negative number, then node i i i is the root node of the set where it is, because no node in the set has a negative ordinal number. And we use the absolute value of the negative number as the number of nodes in this set. For example, if father [ 7 ] = − 4 \texttt{father}[7]=-4 father[7]=−4, node 7 7 7 is the root of the set, which has four elements. The initial father value of the node is − 1 -1 −1 (each node is the root and contains only one element of itself).
So,comes the code:
int find(int x)
{
if(father[x] == x)
return x;
return father[x] = find(father[x]);
}
void kruskal()
{
sort(edge, edge + m, cmp);
for(int i = 0; i < m; i++)
{
int u, v;
u = find(edge[i].u);
v = find(edge[i].v);
if(u == v) continue;
MST += edge[i].w;
father[v] = u;
if(++cnt == n - 1) break;
}
}