def genericSearch(self, rootId): """ Execute a generic search in the graph starting from the specified node. :param rootId: the root node ID (integer). :return: the generic exploration tree. """ if rootId not in self.nodes: return None treeNode = TreeNode(rootId) tree = Tree(treeNode) vertexSet = {treeNode} # nodes to explore markedNodes = {rootId} # nodes already explored while len(vertexSet) > 0: # while there are nodes to explore ... treeNode = vertexSet.pop() # get an unexplored node adjacentNodes = self.getAdj(treeNode.info) for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: # if not explored ... newTreeNode = TreeNode(nodeIndex) newTreeNode.father = treeNode treeNode.sons.append(newTreeNode) vertexSet.add(newTreeNode) markedNodes.add(nodeIndex) # mark as explored return tree
def calculateSubNode(self, rootId): """ Questa funzione, dato un grafo e l'Id di un suo nodo, mi restituisce il numero di nodi raggiungibili a partire dal nodo :param rootId: Id del nodo :return: Numero di elementi che posso raggiungere a partire dal nodo """ # Utilizzo l'algoritmo per la visita generica visto a lezione if rootId not in self.nodes: return None counter = 0 # Contatore dei igli del nodo treeNode = TreeNode(rootId) vertexSet = {treeNode} markedNodes = {rootId} while len(vertexSet) > 0: treeNode = vertexSet.pop() adjacentNodes = self.getAdj(treeNode.info) for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: counter = counter + 1 # Incremento il contatore newTreeNode = TreeNode(nodeIndex) vertexSet.add(newTreeNode) markedNodes.add(nodeIndex) return counter
def findLeaf(self, rootId): """ Questa funzione, dato un grafo e l'id di un nodo, esegue una visita generica. Per ogni foglia, chiama la funzione leafDistance, confrontando i valori ottenuti con quelli della lista massima. :param Id della radice da cui far partire la visita :return: lista contenente le informazioni sul nodo massimo e sul percorso """ max = [0, [], 0, []] # Inizializzo a 0 # max[0] = lunghezza del percorso # max[1] = Lista dei nodi più profondi # max[2] = lista dei nodi visitati # max[3] = Id dei nodi più profondi # Utilizzando l'algoritmo per la visita generica visto a lezione, scansiono l'albero if rootId not in self.nodes: return None treeNode = TreeNode(rootId) vertexSet = {treeNode} markedNodes = [rootId] # Nodi visitati while len(vertexSet) > 0: treeNode = vertexSet.pop() adjacentNodes = self.getAdj(treeNode.info) if len( adjacentNodes ) == 1: # Se il nodo che sto considerando ha solamente un nodo adiacente, dunque è una foglia: lunghezzaPercorso = self.leafDistance( treeNode.info ) # Calcolo la lunghezza del percorso massimo raggiungibile dalla foglia if lunghezzaPercorso[0] > max[ 0]: # Se il percorso è più lungo dell'attuale massimo, imposto i valori della foglia max[0] = lunghezzaPercorso[0] max[1] = lunghezzaPercorso[1] max[3] = max[3] + lunghezzaPercorso[2] if (lunghezzaPercorso[0] == max[0]): for i in lunghezzaPercorso[ 1]: # Per ogni nodo il cui percorso risulta massimo, if ( i.info not in max[3] ): # controllo che non sia già presente nella lista dei nodi massimi max[1] = max[1] + lunghezzaPercorso[ 1] # In caso non sia presente, lo aggiungo alla lista dei nodi "più profondi" for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: newTreeNode = TreeNode(nodeIndex) newTreeNode.father = treeNode treeNode.sons.append(newTreeNode) vertexSet.add(newTreeNode) markedNodes.append(nodeIndex) max[2] = markedNodes # In max[2] avrò la lista dei nodi visitati durante la visita return max
def leafDistance(self, rootId): """ Questa funzione, dato un grafo e l'id di una foglia, restituisce il percoso più lungo e la sua lunghezza. :param rootId: the root node ID (integer). :return: the generic exploration tree. """ lunghezzaPercorso = [0, [], []] # Inizializzo a 0 # lunghezzaPercorso[0] = distanza del nodo dalla radice # lunghezzaPercorso[1] = nodo # lunghezzaPercorso[2] = Id del nodo # Eseguo una visita generica utilizzando l'algoritmo visto a lezione if rootId not in self.nodes: return None treeNode = TreeNode(rootId) vertexSet = {treeNode} markedNodes = [rootId] while len(vertexSet) > 0: treeNode = vertexSet.pop() adjacentNodes = self.getAdj(treeNode.info) if len( adjacentNodes ) == 1: # Quando trovo una foglia, controllo la sua distanza dalla foglia considerata in questo caso come radice if lunghezzaPercorso[ 0] < treeNode.distanza: # Se la foglia ha una distanza superiore a quella dell'attuale percorso, imposto i nuovi valori lunghezzaPercorso[0] = treeNode.distanza lunghezzaPercorso[1] = [] lunghezzaPercorso[1].append(treeNode) lunghezzaPercorso[2].append(treeNode.info) elif lunghezzaPercorso[ 0] == treeNode.distanza: # Altrimenti aggiungo agli altri valori già presenti in lista lunghezzaPercorso[1].append(treeNode) lunghezzaPercorso[2].append(treeNode.info) for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: newTreeNode = TreeNode(nodeIndex) newTreeNode.father = treeNode newTreeNode.distanza = treeNode.distanza + 1 # Incremento la distanza del nodo treeNode.sons.append(newTreeNode) vertexSet.add(newTreeNode) markedNodes.append(nodeIndex) return lunghezzaPercorso # Restituisco la lista con i valori
def mediumNode(self, rootId): """ Questa funzione, dato un grafo e l'id di un nodo, esegue una visita generica. Per ogni foglia, chiama la funzione leafDistance, confrontando i valori ottenuti con quelli della lista massima. :param Id della radice da cui far partire la visita :return: lista contenente le informazioni sul nodo massimo e sul percorso """ max = [0, 0, 0] # Inizializzo a 0 le informazioni riguardo al nodo massimo # max[0] = lunghezza del percorso # max[1] = foglia più profonda # max[2] = lista dei nodi visitati # max[3] = nodi appartenenti al percorso più lungo # Utilizzando l'algoritmo per la visita generica visto a lezione, scansiono l'albero if rootId not in self.nodes: return None treeNode = TreeNode(rootId) vertexSet = {treeNode} markedNodes = [rootId] # Nodi visitati while len(vertexSet) > 0: treeNode = vertexSet.pop() adjacentNodes = self.getAdj(treeNode.info) if len( adjacentNodes) == 1: # Se il nodo che sto considerando ha solamente un nodo adiacente, dunque è una foglia: lunghezzaPercorso = self.leafDistance( treeNode.info) # Calcolo la lunghezza del percorso massimo raggiungibile dalla foglia if lunghezzaPercorso[0] > max[ 0]: # Se il percorso è più lungo dell'attuale massimo, imposto i valori della foglia max[0] = lunghezzaPercorso[0] max[1] = lunghezzaPercorso[1] for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: newTreeNode = TreeNode(nodeIndex) newTreeNode.father = treeNode treeNode.sons.append(newTreeNode) vertexSet.add(newTreeNode) markedNodes.append(nodeIndex) max[2] = markedNodes return max
def prim(graph): """ Prim's algorithm is a greedy algorithm for the computation of the Minimum Spanning Tree (MST). The algorithm assumes a graph implemented as incidence list. This implementation leverages the BinaryTree and PriorityQueue data structures. --- Time Complexity: O(|E|*log(|V|) Memory Complexity: O(|E|) :param graph: the graph. :return the MST, represented as a tree. """ # initialize the root root = 0 # initialize the tree tree = Tree(TreeNode(root)) # MST as a tree mst_nodes = {root} # nodes added to MST # initialize weights current_weight = len(graph.nodes) * [INFINITE] current_weight[root] = 0 mst_weight = 0 # initialize the frontier (priority-queue) pq = PriorityQueue() pq.insert((root, Edge(root, root, 0)), 0) # while the frontier is not empty ... while not pq.isEmpty(): # pop from the priority queue the node u with the minimum weight pq_elem = pq.popMin() node = pq_elem[0] # if node u not yet in MST, update the tree if node not in mst_nodes: edge = pq_elem[1] tree_node = TreeNode(node) tree_father = tree.foundNodeByElem(edge.tail) tree_father.sons.append(tree_node) tree_node.father = tree_father mst_nodes.add(node) mst_weight += edge.weight # for every edge (u,v) ... curr = graph.inc[node].getFirstRecord() while curr is not None: edge = curr.elem # the edge head = edge.head # the head node of the edge # if node v not yet added into the tree, push it into the priority # queue and apply the relaxation step if head not in mst_nodes: weight = edge.weight pq.insert((head, edge), weight) # relaxation step if current_weight[head] > weight: current_weight[head] = weight curr = curr.next return mst_weight, tree
else: nodeList = [[primoElemento, secondoElemento], first] else: # Se il numero di elementi nel percorso è dispari, prendo quello che sta a metà nodeList = [percorso[int(len(percorso) / 2)], int(len(percorso) / 2)] return nodeList def calculateSubNode(self, rootId): counter = 0 # Contatore degli elementi figli del nodo # Utilizzo l'algoritmo per la visita generica visto a lezione if rootId not in self.nodes: return None treeNode = TreeNode(rootId) vertexSet = {treeNode} markedNodes = {rootId} while len(vertexSet) > 0: treeNode = vertexSet.pop() adjacentNodes = self.getAdj(treeNode.info) for nodeIndex in adjacentNodes: if nodeIndex not in markedNodes: counter = counter + 1 # Incremento il contatore newTreeNode = TreeNode(nodeIndex) vertexSet.add(newTreeNode) markedNodes.add(nodeIndex) return counter
def Dijkstra(graph, root): """ Dijkstra's algorithm for the computation of the shortest path. The algorithm assumes a graph implemented as incidence list. This implementation leverages the BinaryTree and PriorityQueue data structures. --- Time Complexity: O(|E|*log(|V|)) Memory Complexity: O() :param graph: the graph. :param root: the root node to start from. :return: the shortest path. """ n = len(graph.nodes) # initialize weights: # d[i] = inf for every i in E # d[i] = 0, if i == root currentWeight = n * [INFINITE] currentWeight[root] = 0 # initialize the tree tree = Tree(TreeNode(root)) mstNodes = {root} mstWeight = 0 # initialize the frontier (priority queue) pq = PriorityQueue() pq.insert((root, Edge(root, root, 0)), 0) # while the frontier is not empty ... while not pq.isEmpty(): # pop from the priority queue the node u with the minimum weight pq_elem = pq.popMin() node = pq_elem[0] # if node not yet in the tree, update the tree if node not in mstNodes: edge = pq_elem[1] treeNode = TreeNode(node) father = tree.foundNodeByElem(edge.tail) father.sons.append(treeNode) treeNode.father = father mstNodes.add(node) mstWeight += edge.weight # for every edge (u,v) ... curr = graph.inc[node].getFirstRecord() while curr is not None: edge = curr.elem # the edge head = edge.head # the head node of the edge # if node v not yet added into the tree, push it into the priority # queue and apply the relaxation step if head not in mstNodes: tail = edge.tail weight = edge.weight currWeight = currentWeight[head] distTail = currentWeight[tail] pq.insert((head, edge), distTail + weight) # relaxation step if currWeight == INFINITE: currentWeight[head] = distTail + weight elif distTail + weight < currWeight: currentWeight[head] = distTail + weight curr = curr.next return currentWeight