package main import "fmt" import "sort" import "math" var vertexVal map[string]int var verticesKeyArray []string var vertices []string var adjcList [][]Edge var vertexValPrim map[string]int // Stores the Minimum spanning Tree var result []Edge type Edge struct { startVertex string endVertex string value int } func heapify(verticesArray []string, root int, length int) { left := (2*root)+1 right := (2*root)+2 smallest := root if (left < length && right <= length && vertexVal[verticesArray[right]] < vertexVal[verticesArray[left]]) { smallest = right } else if (left <= length) { smallest = left } if (vertexVal[verticesArray[root]] > vertexVal[verticesArray[smallest]]) { temp := verticesArray[root] verticesArray[root] = verticesArray[smallest] verticesArray[smallest] = temp heapify(verticesArray, smallest, length) } } func buildHeap() { var verticesArray []string for key := range vertexVal { verticesArray = append(verticesArray, key) } len := len(verticesArray)-1 for parent := int((len-1)/ 2); parent >= 0; parent-- { heapify(verticesArray, parent, len) } verticesKeyArray = verticesArray } func updateHeap(vertex string, length int) { vertexVal[vertex] = length var verticesArray []string for key := range vertexVal { verticesArray = append(verticesArray, key) } len := len(verticesArray)-1 for parent := int((len-1)/ 2); parent >= 0; parent-- { heapify(verticesArray, parent, len) } verticesKeyArray = verticesArray } func containsVertex(vertex string) bool{ _, present := vertexVal[vertex] if (present) { return true } else { return false } } func deleteMin() string{ temp := verticesKeyArray[0] len := len(verticesKeyArray)-1 verticesKeyArray[0] = verticesKeyArray[len] delete(vertexVal, temp) verticesKeyArray = append(verticesKeyArray[:len], verticesKeyArray[len+1:]...) if (len > 0) { heapify(verticesKeyArray, 0, len-1) } return temp } func getWeight(vertex string) int { return vertexVal[vertex] } func empty() bool { if (len(verticesKeyArray) > 0) { return false } else { return true } } func primMST(){ vertexValPrim = make(map[string]int) // Vertex to Edge Map vertexToEdge := make(map[string]Edge) // Assign all the initial values as infinity for all the Vertices. for _, v := range vertices { vertexValPrim[v] = math.MaxInt64 } vertexVal = vertexValPrim // Call buildHeap() to create the MinHeap buildHeap() // Replace the value of start vertex to 0. updateHeap("a",0) // Continue until the Min-Heap is not empty. for !empty() { // Extract minimum value vertex from Map in Heap currentVertex := deleteMin() // Need to get the edge for the vertex and add it to the Minimum Spanning Tree.. // Just note, the edge for the source vertex will not be added. spanningTreeEdge := vertexToEdge[currentVertex] if(spanningTreeEdge != Edge{}) { result = append(result, spanningTreeEdge) } // Get all the adjacent vertices and iterate through them. for _, edge := range getEdges(currentVertex) { adjacentVertex := edge.endVertex // We check if adjacent vertex exist in 'Map in Heap' and length of the edge is with this vertex // is greater than this edge length. if(containsVertex(adjacentVertex) && getWeight(adjacentVertex) > edge.value) { // Replace the edge length with this edge weight. updateHeap(adjacentVertex, edge.value) vertexToEdge[adjacentVertex] = edge } } } } func getEdges(vertex string) []Edge{ var edgeList []Edge i := sort.StringSlice(vertices).Search(vertex) for j := 0; j < len(adjcList[i]); j++ { edgeList = append(edgeList, adjcList[i][j]) } return edgeList } func constructTempList(tmpList *[]Edge, vertex1 string, vertex2 string, edgeVal int) { var edge Edge edge.startVertex = vertex1 edge.endVertex = vertex2 edge.value = edgeVal *tmpList = append(*tmpList, edge) } func constructAdjacencyList(adjcList *[][]Edge, tmpList *[]Edge) { *adjcList = append(*adjcList, *tmpList) *tmpList = nil } func insertVertex(vertex string) { vertices = append(vertices, vertex) } func printEdgeList() { for _, edge := range result { fmt.Println("The Edge between ",edge.startVertex," and ",edge.endVertex," is ",edge.value ) } } func main() { var tempList[]Edge // Adding vertices one by one insertVertex("a") insertVertex("b") insertVertex("c") insertVertex("d") insertVertex("e") // Create Adjacency List with Edges. constructTempList(&tempList, "a", "b", 1) constructTempList(&tempList, "a", "c", 5) constructTempList(&tempList, "a", "d", 4) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "b", "a", 1) constructTempList(&tempList, "b" ,"e", 8) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "c", "a", 5) constructTempList(&tempList, "c", "d", 12) constructTempList(&tempList, "c", "e", 9) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "d", "a", 4) constructTempList(&tempList, "d", "c", 12) constructTempList(&tempList, "d", "e", 3) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "e", "b", 8) constructTempList(&tempList, "e", "c", 9) constructTempList(&tempList, "e", "d", 3) constructAdjacencyList(&adjcList, &tempList) primMST() printEdgeList() }
Let us recapitulate the 3 step process for Prim's Algorithm :
Let us take the below graph, to understand the above code.
The first task is to construct the Graph. So, we need to store the Vertices first.
LinkedList<String> vertices;
And also we know, we have to represent the edges as Adjacency List.
LinkedList<LinkedList<Edge>> adjcList;
Eventually, in the main(...) method. We have initialised the vertices,
insertVertex("a") insertVertex("b") insertVertex("c") insertVertex("d") insertVertex("e")
And, created the Adjacency List with Edges.
// Create Adjacency List with Edges. constructTempList(&tempList, "a", "b", 1) constructTempList(&tempList, "a", "c", 5) constructTempList(&tempList, "a", "d", 4) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "b", "a", 1) constructTempList(&tempList, "b" ,"e", 8) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "c", "a", 5) constructTempList(&tempList, "c", "d", 12) constructTempList(&tempList, "c", "e", 9) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "d", "a", 4) constructTempList(&tempList, "d", "c", 12) constructTempList(&tempList, "d", "e", 3) constructAdjacencyList(&adjcList, &tempList) constructTempList(&tempList, "e", "b", 8) constructTempList(&tempList, "e", "c", 9) constructTempList(&tempList, "e", "d", 3) constructAdjacencyList(&adjcList, &tempList)
Let us understand creation process of Adjacency List. If you already know it, you can skip.
Click Here - For the creation process of Adjacency List.
func constructTempList(tmpList *[]Edge, vertex1 string, vertex2 string, edgeVal int) { var edge Edge edge.startVertex = vertex1 edge.endVertex = vertex2 edge.value = edgeVal *tmpList = append(*tmpList, edge) }
Let us take the example of the Edge (a,b) with length 1 and understand the above code.
constructTempList(&tempList, "a", "b", 1)
Now, if we look at the method definition,
func constructTempList(tmpList *[]Edge, vertex1 string, vertex2 string, edgeVal int)
vertex1 string is initialised with a.
vertex2 string is initialised with b.
edgeVal int is initialised with 1.
The next thing we do is, create an Edge type variable.
var edge Edge
And initialise the var edge Edge variable with the start vertex, end vertex and the length of the Edge.
edge.startVertex = vertex1 edge.endVertex = vertex2 edge.value = edgeVal
And the Edge type variable looks somewhat like,
Now, since we have the Edge (a,b) initialised. The next task would be, to add this Edge to the tmpList.
*tmpList = append(*tmpList, edge)
We repeat the same process for the other adjacent Edges. i.e.
And
constructTempList(&tempList, "a", "c", 5) constructTempList(&tempList, "a", "d", 4)
Then we call the, constructAdjacencyList(&adjcList, &tempList).
func constructAdjacencyList(adjcList *[][]Edge, tmpList *[]Edge) { *adjcList = append(*adjcList, *tmpList) *tmpList = nil }
And as we have seen, there is a 2D Array to store the Adjacency List.
var adjcList [][]Edge
Now, we will be adding the adjacent edges to the first row of adjcList.
And eventually the AdjacencyList with Edges would be created with the 2D Array.
Once we are done creating the AdjacencyList with Edges. The next task would be to call the actual method for Prim's Algorithm,
primMST.primMST();
Before we understand primMST() method in detail. Let us understand the Min-Heap Data Structure.
If you already know Min-Heap Data Structure, you can skip the below part.
Click Here - For Min-Heap Data Structure Recap.
There are four important method in the Min-Heap that are quite important.
They are :
Now, if we look at the primMST() method,
We are creating a variable of map type,
vertexValPrim = make(map[string]int)
And initialising the var vertexVal map[string]int with vertexValPrim.
vertexVal = vertexValPrim
Next, we initialise the Map with the vertices as key and assign the value as math.MaxInt64. This math.MaxInt64 is so large that it could be compared with infinity.
// Assign all the initial values as infinity for all the Vertices. for _, v := range vertices { vertexValPrim[v] = math.MaxInt64 }
So, the Map vertexVal is loaded with values.
Next, we call the buildHeap() method to create the MinHeap, with the values of the Map vertexVal.
minHeap.buildHeap();
func buildHeap() { var verticesArray []string for key := range vertexVal { verticesArray = append(verticesArray, key) } len := len(verticesArray)-1 for parent := int((len-1)/ 2); parent >= 0; parent-- { heapify(verticesArray, parent, len) } verticesKeyArray = verticesArray }
Just remember, Map is not a good Data Structure for Heap. Whereas array is an excellent Data Structure for Heap.
So, we take the keys from the Map and create an Array out of it.
for key := range vertexVal { verticesArray = append(verticesArray, key) }
Then, we take the length of the Array.
len := len(verticesArray)-1
Then, we divide the Array into two parts and run a for loop. Then call the Heapify method.
for parent := int((len-1)/ 2); parent >= 0; parent-- { heapify(verticesArray, parent, len) }
So, what does the heapify(..) method do?
Let us see.
func heapify(verticesArray []string, root int, length int) { left := (2*root)+1 right := (2*root)+2 smallest := root if (left < length && right <= length && vertexVal[verticesArray[right]] < vertexVal[verticesArray[left]]) { smallest = right } else if (left <= length) { smallest = left } if (vertexVal[verticesArray[root]] > vertexVal[verticesArray[smallest]]) { temp := verticesArray[root] verticesArray[root] = verticesArray[smallest] verticesArray[smallest] = temp heapify(verticesArray, smallest, length) } }
Since, all the values of the Map vertexVal are infinity now. So, it doesn't matter how the values will be inserted in the Min-Heap.
For the sake of understanding, let us understand the functionality of heapify(...) method.
So, we have passed the verticesArray, root element and the length of the Array to heapify(...) method.
Then, we have calculated the left, right and root element of the Heap.
left := (2*root)+1 right := (2*root)+2 smallest := root
Since, this is a Min-Heap. The smallest element would be at the root of the Heap.
So, we try initialising the smallest element with its right value.
And the below if condition checks, if the left and right element is less than the length of the Array.
if (left < length && right <= length && vertexVal[verticesArray[right]] < vertexVal[verticesArray[left]]) { smallest = right } else if (left <= length) { smallest = left }
And, if the element on the right side of the Map vertexVal is less than the element on the right side.
vertexVal[verticesArray[right]] < vertexVal[verticesArray[left]]
Then element on the right side of the Map is the smallest element.
smallest = right
And in the else part, we can assume that the element on the left side of the Map has the smallest element.
smallest = left
Now, since we got the smallest element. We need to check if the actual root element is greater than the smallest element or not.
if (vertexVal[verticesArray[root]] > vertexVal[verticesArray[smallest]]) { temp := verticesArray[root] verticesArray[root] = verticesArray[smallest] verticesArray[smallest] = temp heapify(verticesArray, smallest, length) }
And this is where, we swap the contents of the root element and the smallest element. Assuming the element in the root is greater than the smallest element.
Which shouldn't be. As the root element should always be the smallest element.
Then a recursive call is made to heapify(...) method.
heapify(verticesArray, smallest, length)
And the recursion continues until all the elements are arranged in MinHeap.
In the next line, we would update the value of source element a to 0.
// Replace the value of start vertex to 0. updateHeap("a",0)
Now, if we see the updateHeap(...) method,
func updateHeap(vertex string, length int) { vertexVal[vertex] = length var verticesArray []string for key := range vertexVal { verticesArray = append(verticesArray, key) } len := len(verticesArray)-1 for parent := int((len-1)/ 2); parent >= 0; parent-- { heapify(verticesArray, parent, len) } verticesKeyArray = verticesArray }
The first thing we do is, replace the value in the Map vertexVal.
vertexVal[vertex] = length
Then we follow the same process we followed above. i.e. Extract the keys from the Map and convert into an Array.
for key := range vertexVal { verticesArray = append(verticesArray, key) }
Then we call the heapify(...) method. Because we have updated a new value and in that case the Heap has to be rearranged to form a Min-Heap.
Now, that we have understood Min-Heap. Let us understand the details about the actual method that calculates the Minimum Spanning Tree using Prim's Algorithm.
func primMST(){ vertexValPrim = make(map[string]int) // Vertex to Edge Map vertexToEdge := make(map[string]Edge) // Assign all the initial values as infinity for all the Vertices. for _, v := range vertices { vertexValPrim[v] = math.MaxInt64 } vertexVal = vertexValPrim // Call buildHeap() to create the MinHeap buildHeap() // Replace the value of start vertex to 0. updateHeap("a",0) // Continue until the Min-Heap is not empty. for !empty() { // Extract minimum value vertex from Map in Heap currentVertex := deleteMin() // Need to get the edge for the vertex and add it to the Minimum Spanning Tree.. // Just note, the edge for the source vertex will not be added. spanningTreeEdge := vertexToEdge[currentVertex] if(spanningTreeEdge != Edge{}) { result = append(result, spanningTreeEdge) } // Get all the adjacent vertices and iterate through them. for _, edge := range getEdges(currentVertex) { adjacentVertex := edge.endVertex // We check if adjacent vertex exist in 'Map in Heap' and length of the edge is with this vertex // is greater than this edge length. if(containsVertex(adjacentVertex) && getWeight(adjacentVertex) > edge.value) { // Replace the edge length with this edge weight. updateHeap(adjacentVertex, edge.value) vertexToEdge[adjacentVertex] = edge } } } }
Three things to remember :
Now, let us look at the steps involved :
vertexValPrim = make(map[string]int)
vertexToEdge := make(map[string]Edge)
for _, v := range vertices { vertexValPrim[v] = math.MaxInt64 }
var vertexVal map[string]int
vertexVal = vertexValPrim
buildHeap();
updateHeap("a",0);
for !empty() {
// Extract minimum value vertex from Map in Heap
currentVertex := deleteMin()
// Need to get the edge for the vertex and add it to the Minimum Spanning Tree..
// Just note, the edge for the source vertex will not be added.
spanningTreeEdge := vertexToEdge[currentVertex]
if(spanningTreeEdge != Edge{}) {
result = append(result, spanningTreeEdge)
}
// Get all the adjacent vertices and iterate through them.
for _, edge := range getEdges(currentVertex) {
adjacentVertex := edge.endVertex
// We check if adjacent vertex exist in Map in Heap and length of the edge is with this vertex
// is greater than this edge length.
if(containsVertex(adjacentVertex) && getWeight(adjacentVertex) > edge.value) {
// Replace the edge length with this edge weight.
updateHeap(adjacentVertex, edge.value)
vertexToEdge[adjacentVertex] = edge
}
}
}
currentVertex := deleteMin()
spanningTreeEdge := vertexToEdge[currentVertex] if(spanningTreeEdge != Edge{}) { result = append(result, spanningTreeEdge) }
for _, edge := range getEdges(currentVertex) {
adjacentVertex := edge.endVertex
// We check if adjacent vertex exist in Map in Heap and length of the edge is with this vertex
// is greater than this edge length.
if(containsVertex(adjacentVertex) && getWeight(adjacentVertex) > edge.value) {
// Replace the edge length with this edge weight.
updateHeap(adjacentVertex, edge.value)
vertexToEdge[adjacentVertex] = edge
}
}
adjacentVertex := edge.endVertex
if(containsVertex(adjacentVertex) && getWeight(adjacentVertex) > edge.value) { // Replace the edge length with this edge weight. updateHeap(adjacentVertex, edge.value) vertexToEdge[adjacentVertex] = edge }
updateHeap(adjacentVertex, edge.value) Similarly, we put the actual edge i.e. a,b to the value of vertex b in the Map vertexToEdge that stores vertex as key and the Edge associated to it as value. vertexToEdge[adjacentVertex] = edge
The similar process gets repeated and we get the Minimum Spanning Tree using Prim's Algorithm.
Say vertex b has the Edge length 1 that is stored in the Map vertexVal(i.e. Map in Heap) and the actual Edge a,b is stored in the Map vertexToEdge.
The time complexity of Prim's Algorithm for Minimum Spanning Tree is : O(E logV)
Where E is the Edge
And V is the Vertex
The time complexity O(E logV) is only when we will be using the above combination of MIn-Heap and Adjacency List.