Minimum Spanning Trees

Algorithmique Fall semester 2011/12

Acknowledgment: Slides modeled after the course CO226 at Princeton Given: Undirected+connected graph G with positive edge weights Definition: A of G is a connected acyclic subgraph T of G with the same set of vertices as G. Goal: find a minimum weight spanning tree of G. (Minimum spanning tree, or MST)

12

4 10 23 14 7 7 19 11 4 2 9 5

Graph G Given: Undirected+connected graph G with positive edge weights Definition: A spanning tree of G is a connected acyclic subgraph T of G with the same set of vertices as G. Goal: find a minimum weight spanning tree of G. (Minimum spanning tree, or MST)

12

4 10 23 14 7 7 19 11 4 2 9 5

Acyclic, but not spanning Given: Undirected+connected graph G with positive edge weights Definition: A spanning tree of G is a connected acyclic subgraph T of G with the same set of vertices as G. Goal: find a minimum weight spanning tree of G. (Minimum spanning tree, or MST)

12

4 10 23 14 7 7 19 11 4 2 9 5

Spanning, acyclic, but not connected Given: Undirected+connected graph G with positive edge weights Definition: A spanning tree of G is a connected acyclic subgraph T of G with the same set of vertices as G. Goal: find a minimum weight spanning tree of G. (Minimum spanning tree, or MST)

12

4 10 23 14 7 7 19 11 4 2 9 5

Spanning, connected, but not acyclic Given: Undirected+connected graph G with positive edge weights Definition: A spanning tree of G is a connected acyclic subgraph T of G with the same set of vertices as G. Goal: find a minimum weight spanning tree of G. (Minimum spanning tree, or MST)

12

4 10 23 14 7 7 19 11 4 2 9 5

Spanning tree of cost 4+7+11+10+4+7+5 = 48 Examples Example 1: Communication Networks

A multinational company wants to lease communication lines between its various locations. Example 1: Communication Networks

A multinational company wants to lease communication lines between its various locations. Example 1: Communication Networks

A multinational company wants to lease communication lines between its various locations. Example 1: Communication Networks

120

400

100

19 90 10 20 10 12 122 10 10 19 5 19 10 10 18 200 20 10 90 80 100 20 50 18 20 540 120 90 90 20 21 200 200 30 43 10

20 25 152

Each communication line comes with its own price tag. Company wants to spend the least amount of money, and have all its branches connected.

Solution given by a MST on the graph. (Why?) Example 2: Clustering

Edge values equal to distances of nodes

Find “clusters” of nodes. Example 2: Clustering

Possible solution: Find MST. Eliminate “fat” edges. Example 2: Clustering

Possible solution: Find MST. Eliminate “fat” edges.

Note: this is a “heuristic” algorithm. Needs analysis. Example 3: Dendritic Structures in the Brain

Problem: Reconstruct shape of neurons from noisy microscopy data via automatic tools

http://cvlab.epfl.ch/research/medical/neurons/ Example 3: Dendritic Structures in the Brain

Dendrite tracking in microscopic images using minimum spanning trees and localized EM -- by Fleuret and Fua Example 4: Phylogenetic Trees

Genetic variability and population structure of endangered Panax ginseng in the Russian Primorye -- by Zhuravlev et al Algorithms Cuts

Cut: A (A,B) in a graph G=(V,E) is a partition of V into two nonempty sets A and B. Crossing edge: Any edge connecting a vertex in A to a vertex in B.

Crossing edges A B Cut Property

Cut C=(A,B), tree T on A which is part of MST, e crossing edge of minimum weight. Then there is MST M containing e and T.

Crossing edge of minimum weight A MST Cut Property

Cut C=(A,B), tree T on A which is part of MST, e crossing edge of minimum weight. Then there is MST M containing e and T. Proof: • Take MST containing T

• Add crossing edge e of min weight to MST e • This creates a cycle

e • Cycle has one other crossing edge f • Weight of f is at least equal to that of e f

e Replace f by e in the MST • f • This gives new MST which contains e • This means that the weights of e and f have been equal Prim’s Algorithm http://inserv.math.muni.cz/biografie/vojtech_jarnik.html http://www.ithistory.org/honor_roll/fame-detail.php?recordID=882 http://en.wikipedia.org/wiki/Edsger_W._Dijkstra Voijtech Jarnik Robert Prim Edsger Dijkstra 1897-1970 1921 - 1930-2002 Prim’s Algorithm

Start with any vertex v, set tree T to singleton v. Greedily grow tree T: at each step add to T a minimum weight edge with exactly one endpoint in T.

Edges connected to exactly one tree node Tree edges Tree nodes

v

etc Prim’s Algorithm

Why does it work? T is always a subtree of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

Singleton v is part of a MST v

Step: use cut property

Crossing edge of minimum weight

In MST by hypothesis In MST by cut property Prim’s Algorithm 10

2 3 5 4 8 12 10 2 23

19 18 22 12

17 Prim’s Algorithm 10

2 3 5 4 8 12 10 2 23

19 18 22 12

17 Prim’s Algorithm 10

2 3 5 4 8 12 10 2 23

19 18 22 12

17 Prim’s Algorithm 10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm 10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Prim’s Algorithm

10

2 3 5 4 8 12 10 2 23 16 19 18 22 12

17 Implementation Challenge

How do we find minimum crossing edge at every iteration?

Check all the outgoing edges: O(|E|) comparisons at every iteration O(|E| |V|) running time in total

More clever data structure:

• For every node w, keep value dist(w) that measures the “distance” of w from current tree • At the start, dist(v) = 0, and dist(w) = infinity for all other w • When a new node u is added to tree, check whether the neighbors of u decrease their distance to tree; if so, decrease distance.

Maintain a min- for the nodes and their distances. Implementation

(1) dist(v) = 0, dist(w) = for w = v, pred(v) = NULL ∞ ￿ (2) Create min-priority queue Q for V with respect to dist (3) While Q is not empty do Q contains all nodes that are not yet covered by the MST (a) u = deleteMin(Q) u is node with smallest distance to the current tree (b) if ( u is not marked) then

(i) Mark u u is now covered (ii) For all neighbors w of u do a. if ( dist(w) > weight of edge (u,w) and w not marked) then i. dist(w) = weight of edge (u,w) Update distance of neighbors of u ii. pred(w) = u Predecessor of w in the tree iii. Sift up w in Q Multiple copies of w may be present in Q, but only one gets marked (4) Output tree {(pred(v),v) | v in V} Analysis

(1) dist(v) = 0, dist(w) = for w = v, pred(v) = NULL ∞ ￿ (2) Create min-priority queue Q for V with respect to dist O(|V|)

(3) While Q is not empty do < |E| times (each vertex gets added to Q at most its degree times)

(a) u = deleteMin(Q) O(log(|Q|)) = O(log(|E|)) (b) if ( u is not marked) then (i) Mark u (ii) For all neighbors w of u do a. if ( dist(w) > weight of edge (u,w) and w not marked) then i. dist(w) = weight of edge (u,w) at most |E| times in total ii. pred(w) = u

iii. Sift up w in Q O(log(|Q|)) = O(log(|E|)) (4) Output tree {(pred(v),v) | v in V}

O(|E| log(|E|)) for connected graphs. Kruskal’s Algorithm http://www.voteview.com/ideal_point_Non_Metric_MDS.htm Joseph B. Kruskal 1928-2010 Kruskal’s Algorithm

Maintains forest which will become a MST at the end. (1)Start from empty tree T (2)Consider edges in ascending order of cost. Add next edge in list to T if it doesn’t create a cycle. Several components 2 2 2 2 0 6 0 6 0 6 0 6

7 7 7 7 u v weight 1 1 1 1 3 5 0.18 1 7 0.21 3 3 3 3 6 7 0.25 5 4 5 4 5 4 5 4 0 2 0.29 3-5 1-7 6-7 0-2 0 7 0.31 0 1 0.32 2 2 2 3 4 0.34 0 6 0 6 0 6 4 5 0.40 4 7 0.46 7 7 7 0 6 0.51 1 1 1 4 6 0.51 0 5 0.6 3 3 3

5 4 5 4 5 4 0-7 3-4 4-7 Kruskal’s Algorithm

Why does it work? T is always a sub-forest of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

T is a union of singleton vertices

Step: by hypothesis, current T is sub-forest of a MST.

Blue edges are part of a MST, but are not added yet

e

Blue edges have already been added

Edge e is edge of minimum weight that doesn’t create cycles (among the black edges) Kruskal’s Algorithm

Why does it work? T is always a sub-forest of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

T is a union of singleton vertices

Step: by hypothesis, current T is sub-forest of a MST.

Blue edges are part of a MST, but are not added yet

e

Blue edges have already been added

Weight of e is smaller than the weights of the blue edges Kruskal’s Algorithm

Why does it work? T is always a sub-forest of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

T is a union of singleton vertices

Step: by hypothesis, current T is sub-forest of a MST.

Blue edges are part of a MST, but are not added yet

e

Blue edges have already been added

Exchanging one of the blue edges with e creates another MST which contains T as a sub-forest, and also contains e, hence contains the new T. Kruskal’s Algorithm

Why does it work? T is always a sub-forest of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

T is a union of singleton vertices

Step: by hypothesis, current T is sub-forest of a MST.

Blue edges are part of a MST, but are not added yet

e

Blue edges have already been added

Cost of the new T is smaller than or equal to cost of the old MST, since weight of e is smaller than or equal to the weight of the blue edges. Kruskal’s Algorithm

Why does it work? T is always a sub-forest of a MST Induction on number of nodes in T. Final T is MST by this result. Start: trivial

T is a union of singleton vertices

Step: by hypothesis, current T is sub-forest of a MST.

Blue edges are part of a MST, but are not added yet

e

Blue edges have already been added

Hence, new spanning tree is part of a MST, and hence T and e belong to a MST. Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 2 4 1 3 2 2 3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 4 1 3

3 6 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 4 1 3

3 7 5 8 3 1 2 2 1 4 5 1 3 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 3 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 4 1

3 7 5 8 3 1 2 2 1 4 5 1 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1 4 1

3 7 5 8 3 1 2 2 1 4 5 1 3 3 2 1 4 7 8 3 7 14 6 5 7 19 4 4 1

2 1 5 12 3 2 3

9 4 5 10 4 8 Kruskal’s Algorithm

19

10

1 1 12 12 1

1

3 7 5 8 3 1 2 2 1

5 1 3 3 2 1 7 8 3 7 14 6 5 7 19 4 1

2 1 5 12 3 2 3

9 4 5 10

8 Kruskal’s Algorithm All nodes covered now.

19

10

1 1 12 12 1

1

3 7 8 3 1 2 2 1

1 3 3 2 1 7 8 3 7 14 6

7 19 4 1

2 1 5 12 3 2 3

9 4 10

8 Kruskal’s Algorithm Minimum spanning tree

1 1 1

1

3

3 1 2 2 1

1 3 3 2 1

3

4 1

2 1 5 3 2 3

4 Kruskal’s Algorithm

1

1

2

3 1 1 1 1 1 3

1 1 2

3 1 2

2 3 2

3 3 5 3 4

4 Implementation Challenge

How do we check whether addition of a new edge creates a cycle?

Note: if G=(V,E) is the original graph, and T is the set of edges already created, then we are looking for a cycle in the graph F = (V,T) (not in G). e=(u,v) creates cycle iff connected of u = connected component of v

We could check whether the connected components are equal by doing a DFS starting from u and checking whether we reach v. Implementation: 1st Version

(1) Create min-priority queue Q for E with respect to weight ...... Sort the edges (2) For all v in V set pred(v)=NULL

(3) Set T to the empty set ...... Initial forest doesn’t have edges

(4) While Q is not empty do ...... Continue until all edges processed (can stop when |T|=|V|-1)

(a) (u,v) = deleteMin(Q) ...... Take smallest edge off the queue

(b) Run DFS starting from u on T ...... Check whether edge creates cycle (c) If v is not in the connected component of u then (i) if pred(u)=NULL set pred(u)=v else pred(v)=u (ii) Add edge (u,v) to T (5) Output T Implementation: 1st Version

(1) Create min-priority queue Q for E with respect to weight O(|E|) (2) For all v in V set pred(v)=NULL (3) Set T to the empty set (4) While Q is not empty do < |E| times (a) (u,v) = deleteMin(Q) O(log(|E|)

(b) Run DFS starting from u on T O(|T|) = O(|V|) (c) If v is not in the connected component of u then (i) if pred(u)=NULL set pred(u)=v else pred(v)=u (ii) Add edge (u,v) to T (5) Output T

O(|E| |V|) total. Better?

Check whether |T|=|V|-1 Just sort edges with respect to weight. No need for Q.

(1) Create min-priority queue Q for E with respect to weight (2) For all v in V set pred(v)=NULL (3) Set T to the empty set (4) While Q is not empty do (a) (u,v) = deleteMin(Q) (b) Run DFS starting from u on T

(c) If v is not in the connected component of u then ???????? (i) if pred(u)=NULL set pred(u)=v else pred(v)=u (ii) Add edge (u,v) to T (5) Output T

Just take next edge off the list What should we do with this?

Still O(|E| |V|) total. Data Structure

Need a good data structure to check whether components are equal.

This is done via the Union-Find data structure. Union-Find Dynamic Graph Model

We have a graph G on n vertices 0,1,....,n-1.

Edges are revealed one-by-one.

Want to keep track of the connected components of the graph as edges are revealed.

Introduce data structure UF on the set of vertices which keeps track of the components.

Operations on UF: • Union(a,b): join the components of a and b. • Connected(a,b): returns true iff a and b are in the same component. Example

union(2,5)

0 1 2 3 0 1 2 3

4 5 6 7 4 5 6 7

Connected(0,6)=false

Connected(2,3)=true Quick Find

Data Structure: • Integer array id[] of size n • Interpretation: p and q are connected iff they have the same id

i 0 1 2 3 4 5 6 7 8 9 5 and 6 are connected id[i] 0 1 9 9 9 6 6 7 8 9 2,3,4, and 9 are connected

0 1 2 3 4

5 6 7 8 9 Quick Find

Data Structure: • Integer array id[] of size n • Interpretation: p and q are connected iff they have the same id

i 0 1 2 3 4 5 6 7 8 9 5 and 6 are connected id[i] 0 1 9 9 9 6 6 7 8 9 2,3,4, and 9 are connected

Connected(p,q): true, iff p and q have same id id[3]=id[9]=6 Connected(3,9)=true Quick Find

Data Structure: • Integer array id[] of size n • Interpretation: p and q are connected iff they have the same id

i 0 1 2 3 4 5 6 7 8 9 5 and 6 are connected id[i] 0 1 9 9 9 6 6 7 8 9 2,3,4, and 9 are connected

Connected(p,q): true, iff p and q have same id id[3]=id[9]=6 Connected(3,9)=true

Union(p,q): to merge the components of p and q, change all entries whose id[] equals id[p] to id[q].

i 0 1 2 3 4 5 6 7 8 9 After union(2,5) id[i] 0 1 6 6 6 6 6 7 8 6

Problem: many values can change Example

id[p] and id[q] differ, so union() changes entries equal to id[p] to id[q] (in red)

id[p] and id[q] match, so no change Too Slow

Count number of array accesses

algorithm init union connected

Quick-find n n 1

Defect: unions are too expensive Quick-Union

Data Structure: • Integer array id[] of size n • Interpretation: id[i] is parent of i Keep going until no change • Root of i is id[id[id[...... id[i].....]]].

i 0 1 2 3 4 5 6 7 8 9 0 1 9 6 7 8 id[i] 0 1 9 4 9 6 6 7 8 9 2 4 5 q

3’s root is 9, 5’s root is 6 3 p Quick-Union

Data Structure: • Integer array id[] of size n • Interpretation: id[i] is parent of i Keep going until no change • Root of i is id[id[id[...... id[i].....]]].

i 0 1 2 3 4 5 6 7 8 9 0 1 9 6 7 8 id[i] 0 1 9 4 9 6 6 7 8 9 2 4 5 q

3’s root is 9, 5’s root is 6 Connected(p,q): Check whether p and q have same root 3 p Connected(3,5)=false

Union(p,q): to merge the components of p and q, set the id of p’s root to the id of q’s root 0 1 6 7 8

9 i 0 1 2 3 4 5 6 7 8 9 5 q

id[i] 0 1 9 4 9 6 6 7 8 6 2 4 union(3,5) Only one value changes 3 p Example Also Too Slow

Count number of array accesses

algorithm init union connected

Quick-find n n 1

Quick-union n n n Worst case

Quick-find defect: unions are too expensive

Quick-union defects: • Trees can get tall • Connected() too expensive (could be n array accesses) Union-Find Data Structure

Data Structure: • Integer array id[] of size n • Interpretation: id[i] is parent of i • Root of i is id[id[id[...... id[i].....]]]. • size[j] is size of connected component of j (if j is root)

i 0 1 2 3 4 5 6 7 8 9 0 1 9 6 7 8 id[i] 0 1 9 4 9 6 6 7 8 9 2 4 5 size[i] 1 1 x x x x 2 1 1 4

3

Size may not be accurate for non-roots Union-Find Data Structure

Data Structure: • Integer array id[] of size n • Interpretation: id[i] is parent of i • Root of i is id[id[id[...... id[i].....]]]. • size[j] is size of connected component of j (if j is root)

Connected(p,q): Check whether p and q have same root

CompSize(p): to find the size of the component of p, find p’s root r and return size[r].

Union(p,q): to merge the components of p and q, set the id of p’s root to the id of q’s root if CompSize(p) smaller than CompSize(q). Otherwise, set the id of q’s root to the id of p’s root. Avoiding Tall Trees

Quick-union

Union-find Example Example

Quick-union

Union-find UF.init(n) ...... Initializes the data structure for i from 0 to n-1 do set id[i]=i, size[i]=1

UF.FindRoot(i) ...... Finds the root of i while id[i] is not equal to i do set id[i] = i return i

UF.Connected(i,j) ...... Connecte d() if UF.FindRoot(i) = UF.FindRoot(j) then return true else return false

UF.union(i,j)...... union() Set p=UF.FindRoot(i), q = UF.FindRoot(j) if p is not equal to q then if size[p] < size[q] then Set id[p] = q and size[p] = size[p]+size[q] else Set id[q]=p and size[q] = size[p]+size[q] Analysis

UF.init(n) O(n) for i from 0 to n-1 do set id[i]=i, size[i]=1

UF.FindRoot(i) Order of height of the tree while id[i] is not equal to i do representing component of i set id[i] = i return i

UF.Connected(i,j) if UF.FindRoot(i) = UF.FindRoot(j) then Same complexity as FindRoot return true else return false

UF.union(i,j) Set p=UF.FindRoot(i), q = UF.FindRoot(j) Same complexity as FindRoot if p is not equal to q then if size[p] < size[q] then Set id[p] = q and size[p] = size[p]+size[q] else Set id[q]=p and size[q] = size[p]+size[q] Analysis

What is the height of the trees formed in terms of their size?

For Quick-union the trees can have height linear in their size. This is responsible for the bad performance of root-finding.

Theorem: for the union-find data structure the height of the tree representing a component is at most log2(s) where s is the number of nodes in the tree.

algorithm init union connected

Quick-find n n 1

Quick-union n n n

Union-find n log(n) log(n) Worst case Proof

Induction on size of the tree.

Height changes only during the union operation. T union of trees T1 and T2

| T | = | T1 | + | T2 |

Assume | T1 | | T2 |, h1 = height(T1), h2 = height(T2), h = height(T) ≤ Then h = max{h1+1,h2}

Case 1: h1 < h2 . T2 h=h2 h1 So h = h2 log2(| T2 |) < log2(| T |) T1 ≤ Induction hypothesis

Case 2: h2 h1. ≤ So h = h1+1 log2(| T1 |)+log2(2) = log2(2 | T1 |) log2( | T1 | + | T2 | ) = log2(| T |) ≤ ≤ Induction hypothesis

T2 h=1+h1 h1 T1 Improvement

Path Compression: Immediately after computing the root of p set the id of each examined node to point to the root.

0 0

1 2 9 6 3 1 2

3 4 5 11 12 8 4 5 7

UF.FintRoot(9) 6 7 10

8 9 p

10 11 12 Example

1 linked to 6 because of path compression

7 linked to 6 because of path compression Does it Work?

Theorem: Starting from an empty data structure, any sequence of M union-find operations on N objects makes at most proportional to N+M log*(N) array accesses.

Without proof.

log*(n) = iterated logarithm of n n log*(n) (1,2] 1 (2,4] 2 (4,16] 3 (16,216] 4 (216,265536] 5 Back to MST’s Upgraded Version of Kruskal’s Algorithm

(1) Create sorted ascending list L of edges with respect to weight (2) UF.Init(|V|) (3) Set T to the empty set (4) for i from 0 to |E|-1 do (a) (u,v) = L[i] These two steps use the UF.FindRoot routine (b) If not UF.Connected(u,v) then twice. Can make it more efficient by letting UF.union return a boolean value which is true (i) UF.union(u,v) iff u and v are connected. (ii) Add edge (u,v) to T (5) Output T Upgraded Version

(1) Create sorted ascending list L of edges with respect to weight O(|E| log(|E|) (2) UF.Init(|V|) O(|V|) (3) Set T to the empty set (4) for i from 0 to |E|-1 do (a) (u,v) = L[i]

(b) If not UF.Connected(u,v) then O(log(|V|) (i) UF.union(u,v) O(log(|V|)

(ii) Add edge (u,v) to T O(1) (5) Output T

O(|E| log(|E|) + |V| + |E|log(|E|) ) = O(|E| log|E|) total.