Kruskal's algorithm for MST

A simple implementation of minimum spanning tree based on Kruskal's algorithm

Kruskal's algorithm for MST
package main

import (
	"bufio"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
)

// links are treated seperately from nodes, which is consistent
// with what we need to compute Kruskal's algorithm. Other graph-based
// might need a certain implementation where links are perceived as
// something like map[*Node][]*Node
type Link struct {
	src    int
	dst    int
	weight int
}

// father is used to show set membership. Different nodes are
// members of a same set if they have identical fathers.
type Node struct {
	index  int
	father *Node
}

func (n *Node) FindSet() *Node {
	if n.father == nil {
		return n
	}
	return n.father.FindSet()
}

// sets represented by two nodes n and dst are unified into one set
// by assigning the same father to their prevoius fathers.
func (n *Node) Union(dst *Node) {
	f := Node{index: -1}
	n.FindSet().father = &f
	dst.FindSet().father = &f
}

type WeightedUndirectedGraph struct {
	nodes []Node
	links []Link
}

// we need to sort the links of the graph based on their weight to
// compue Kruskal's algorithm. We have implemented sort.Interface
// for the graph and then used "sort" package to do so.
func (wug *WeightedUndirectedGraph) Len() int {
	return len(wug.links)
}

func (wug *WeightedUndirectedGraph) Swap(i, j int) {
	wug.links[i], wug.links[j] = wug.links[j], wug.links[i]
}

func (wug *WeightedUndirectedGraph) Less(i, j int) bool {
	return wug.links[i].weight < wug.links[j].weight
}



func (wug *WeightedUndirectedGraph) AddLink(link []int) {
	wug.links = append(wug.links, Link{src: link[0], dst: link[1], weight: link[2]})
	wug.links = append(wug.links, Link{src: link[1], dst: link[0], weight: link[2]})
}

func (wug *WeightedUndirectedGraph) AddBatchLink(links [][]int) {
	for _, link := range links {
		wug.AddLink(link)
	}
}


func (wug *WeightedUndirectedGraph) Kruskal() []Link {
	sort.Sort(wug)

	var MinimumSpanningTree []Link
	for _, link := range wug.links {
		if link.src <= link.dst {
			if wug.nodes[link.src].FindSet() != wug.nodes[link.dst].FindSet() {
				MinimumSpanningTree = append(MinimumSpanningTree, link)
				wug.nodes[link.src].Union(&wug.nodes[link.dst])
			}
		}
	}
	return MinimumSpanningTree
}

func (wug *WeightedUndirectedGraph) Print() {

	for _, link := range wug.links {
		if link.src <= link.dst {
			fmt.Printf("%d - %d : %d\n", link.src, link.dst, link.weight)
		}
	}

}

func NewGraph(size int) *WeightedUndirectedGraph {

	var nodes []Node
	for i := 0; i < size; i++ {
		nodes = append(nodes, Node{index: i})
	}

	return &WeightedUndirectedGraph{
		nodes: nodes,
	}
}

func main() {

	// first line contains the number of nodes N
	// the M subsequent lines with the x y w format each describe a link from node x to y with weight w
	input := `9
0 1 4
0 2 8
1 2 11
2 3 7
1 4 8
2 5 1
3 4 2
3 5 6
4 6 7
4 7 4
5 7 2
6 7 14
6 8 9
7 8 10`

	r := strings.NewReader(input)

	reader := bufio.NewReaderSize(r, 1024*1024)

	nTmp, err := ReadLine(reader)
	if err != nil {
		panic(err)
	}

	n, err := strconv.Atoi(nTmp)
	if err != nil {
		panic(err)
	}

	var links [][]int
	for {

		line, err := ReadLine(reader)
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(fmt.Sprintf("%s \n %v", line, err))
		}

		lineTmp := strings.Split(line, " ")

		var link []int
		for _, itemTmp := range lineTmp {
			item, err := strconv.Atoi(itemTmp)
			if err != nil {
				panic(fmt.Sprintf("%s \n %v", itemTmp, err))
			}
			link = append(link, item)
		}
		links = append(links, link)
	}

	grph := NewGraph(n)
	grph.AddBatchLink(links)
	grph.Print()
	fmt.Println(grph.Kruskal())
}

func ReadLine(r *bufio.Reader) (string, error) {
	line, _, err := r.ReadLine()

	return string(line), err
}