|
| 1 | +# Implementation of Kruskal's Algorithm |
| 2 | + |
| 3 | +# this is a greedy algorithm to find a MST (Minimum Spanning Tree) of a given connected, undirected graph. graph |
| 4 | + |
| 5 | +# So I am implementing the graph using adjacency list, as the user wont be |
| 6 | +# entering too many nodes and edges.The adjacency matrix is a good implementation |
| 7 | +# for a graph when the number of edges is large.So i wont be using that here |
| 8 | + |
| 9 | +# Vertex, which will represent each vertex in the graph.Each Vertex uses a dictionary |
| 10 | +# to keep track of the vertices to which it is connected, and the weight of each edge. |
| 11 | +class Vertex: |
| 12 | + |
| 13 | + # Initialze a object of this class |
| 14 | + # we use double underscore |
| 15 | + def __init__(self, key): |
| 16 | + # we identify the vertex with its key |
| 17 | + self.id = key |
| 18 | + # this stores the info about the various connections any object |
| 19 | + # (vertex) of this class has using a dictionary which is called connectedTo. |
| 20 | + # initially its not connected to any other node so, |
| 21 | + self.connectedTo={} |
| 22 | + |
| 23 | + # Add the information about connection between vertexes into the dictionary connectedTo |
| 24 | + def addNeighbor(self,neighbor,weight=0): |
| 25 | + # neighbor is another vertex we update the connectedTo dictionary ( Vertex:weight ) |
| 26 | + # with the information of this new Edge, the key is the vertex and |
| 27 | + # the edge's weight is its value. This is the new element in the dictionary |
| 28 | + self.connectedTo[neighbor] = weight |
| 29 | + |
| 30 | + # Return a string containing a nicely printable representation of an object. |
| 31 | + def __str__(self): |
| 32 | + return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo]) |
| 33 | + |
| 34 | + # Return the vertex's self is connected to in a List |
| 35 | + def getConnections(self): |
| 36 | + return self.connectedTo.keys() |
| 37 | + |
| 38 | + # Return the id with which we identify the vertex, its name you could say |
| 39 | + def getId(self): |
| 40 | + return self.id |
| 41 | + |
| 42 | + # Return the value (weight) of the edge (or arc) between self and nbr (two vertices) |
| 43 | + def getWeight(self,nbr): |
| 44 | + return self.connectedTo[nbr] |
| 45 | + |
| 46 | +# The Graph class contains a dictionary that maps vertex keys to vertex objects (vertlist) and a count of the number of vertices in the graph |
| 47 | +class Graph: |
| 48 | + |
| 49 | + def __init__(self): |
| 50 | + self.vertList = {} |
| 51 | + self.numVertices = 0 |
| 52 | + |
| 53 | + |
| 54 | + # Returns a vertex which was added to the graph with given key |
| 55 | + def addVertex(self,key): |
| 56 | + self.numVertices = self.numVertices + 1 |
| 57 | + # create a vertex object |
| 58 | + newVertex = Vertex(key) |
| 59 | + # set its key |
| 60 | + self.vertList[key] = newVertex |
| 61 | + return newVertex |
| 62 | + |
| 63 | + # Return the vertex object corresponding to the key - n |
| 64 | + def getVertex(self,n): |
| 65 | + if n in self.vertList: |
| 66 | + return self.vertList[n] |
| 67 | + else: |
| 68 | + return None |
| 69 | + |
| 70 | + # Returns boolean - checks if graph contains a vertex with key n |
| 71 | + def __contains__(self,n): |
| 72 | + return n in self.vertList |
| 73 | + |
| 74 | + # Add's an edge to the graph using addNeighbor method of Vertex |
| 75 | + def addEdge(self,f,t,cost=0): |
| 76 | + # check if the 2 vertices involved in this edge exists inside |
| 77 | + # the graph if not they are added to the graph |
| 78 | + # nv is the Vertex object which is part of the graph |
| 79 | + # and has key of 'f' and 't' respectively, cost is the edge weight |
| 80 | + if f not in self.vertList: |
| 81 | + nv = self.addVertex(f) |
| 82 | + if t not in self.vertList: |
| 83 | + nv = self.addVertex(t) |
| 84 | + # self.vertList[f] gets the vertex with f as key, we call this Vertex |
| 85 | + # object's addNeighbor with both the weight and self.vertList[t] (the vertice with t as key) |
| 86 | + self.vertList[f].addNeighbor(self.vertList[t], cost) |
| 87 | + |
| 88 | + # Return the list of all key's corresponding to the vertex's in the graph |
| 89 | + def getVertices(self): |
| 90 | + return self.vertList.keys() |
| 91 | + |
| 92 | + # Returns an iterator object, which contains all the Vertex's |
| 93 | + def __iter__(self): |
| 94 | + return iter(self.vertList.values()) |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | +# Now lets make the graph |
| 99 | +the_graph=Graph() |
| 100 | + |
| 101 | +print "enter the number of nodes in the graph" |
| 102 | +no_nodes=int(raw_input()) |
| 103 | + |
| 104 | +# setup the nodes |
| 105 | +for i in range(no_nodes): |
| 106 | + print "enter the Node no:"+str(i+1)+"'s key" |
| 107 | + the_graph.addVertex(raw_input()) |
| 108 | + |
| 109 | +print "enter the number of edges in the graph" |
| 110 | +no_edges=int(raw_input()) |
| 111 | + |
| 112 | + |
| 113 | +# setup the edges |
| 114 | +for i in range(no_edges): |
| 115 | + print "For the Edge no:"+str(i+1) |
| 116 | + print "of the 2 nodes involved in this edge \nenter the first Node's key" |
| 117 | + node1_key=raw_input() |
| 118 | + print "\nenter the second Node's key" |
| 119 | + node2_key=raw_input() |
| 120 | + print "\nenter the cost (or weight) of this edge (or arc) - an integer" |
| 121 | + cost=int(raw_input()) |
| 122 | + # add the edge with this info |
| 123 | + the_graph.addEdge(node1_key,node2_key,cost) |
| 124 | + the_graph.addEdge(node2_key,node1_key,cost) |
| 125 | + |
| 126 | +print "enter the maximum weight possible for any of edges in the graph" |
| 127 | +max_weight=int(raw_input()) |
| 128 | + |
| 129 | +# graph DONE - start MST finding |
| 130 | + |
| 131 | +# step 1 : Take all edges and sort them |
| 132 | + |
| 133 | +# Time Complexity of Solution: |
| 134 | +# Best Case O(n+k); Average Case O(n+k); Worst Case O(n+k), |
| 135 | +# where n is the size of the input array and k means the |
| 136 | +# values(weights) range from 0 to k. |
| 137 | +def counting_sort(weights,max_weight): |
| 138 | + # these k+1 counters are made here is used to know how many times each value in range(k+1) (0 to k) repeats |
| 139 | + counter=[0]*(k+1) |
| 140 | + for i in weights: |
| 141 | + # if you encounter a particular number increment its respective counter |
| 142 | + counter[i] += 1 |
| 143 | + # no idea why ndx?! it is the key for the output array |
| 144 | + ndx=0 |
| 145 | + # traverse though the counter list |
| 146 | + for i in range(len(counter)): |
| 147 | + # if the count of i is more than 0, then append that many 'i' |
| 148 | + while 0<counter[i]: |
| 149 | + # rewrite the array which was given to make it ordered |
| 150 | + weights[ndx] = i |
| 151 | + ndx += 1 |
| 152 | + # reset the counter back to the set of zero's |
| 153 | + counter[i] -= 1 |
| 154 | + |
| 155 | +# now we have a optimal sorting function in hand, lets sort the list of edges. |
| 156 | +# a dictionary with weights of an edge and the vertexes involved in that edge. |
| 157 | +vrwght={} |
| 158 | +for ver1 in the_graph: |
| 159 | + print "ver1 = " |
| 160 | + print ver1 |
| 161 | + for ver2 in ver1.getConnections(): |
| 162 | + vrwght[ver1.connectedTo[ver2]]=[ver1.getId(),ver2.getId()] |
| 163 | + |
| 164 | +print vrwght |
| 165 | +print vrwght.keys() |
| 166 | +print the_graph.getVertices() |
| 167 | +print the_graph.getVertices() |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | + |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | + |
| 180 | + |
| 181 | + |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | + |
0 commit comments