From 370adb12342e5be8adc90c7cf3b3efbb6d473b0d Mon Sep 17 00:00:00 2001 From: Safak Date: Tue, 15 Apr 2025 13:36:57 +0200 Subject: [PATCH] BFS and DFS --- graph.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.py | 2 ++ 3 files changed, 140 insertions(+) create mode 100644 graph.py create mode 100644 main.py create mode 100644 utils.py diff --git a/graph.py b/graph.py new file mode 100644 index 0000000..9c1fa6c --- /dev/null +++ b/graph.py @@ -0,0 +1,65 @@ +from prettytable import PrettyTable +from utils import * + +class Node: + + def __init__(self, name): + self.parent = None + self.name = name + self.edges = [] + self.value = 0 + +class Edge: + + def __init__(self, edge): + self.start = edge[0] + self.end = edge[1] + self.value = edge[2] + + +class Graph: + + def __init__(self, node_list, edges): + self.nodes = [] + for name in node_list: + self.nodes.append(Node(name)) + + for e in edges: + e = (getNode(e[0],self.nodes), getNode(e[1], self.nodes), e[2]) + + self.nodes[next((i for i,v in enumerate(self.nodes) if v.name == e[0].name), -1)].edges.append(Edge(e)) + self.nodes[next((i for i,v in enumerate(self.nodes) if v.name == e[1].name), -1)].edges.append(Edge((e[1], e[0], e[2]))) + + + def print(self): + node_list = self.nodes + + t = PrettyTable([' '] +[i.name for i in node_list]) + for node in node_list: + edge_values = ['X'] * len(node_list) + for edge in node.edges: + edge_values[ next((i for i,e in enumerate(node_list) if e.name == edge.end.name) , -1)] = edge.value + t.add_row([node.name] + edge_values) + print(t) + +class Queue: + def __init__(self, type): + self.type = type + self.items = [] + + def empty(self): + return len(self.items) == 0 + + def pop(self): + if not self.empty(): + if self.type=='FIFO': + return self.items.pop(0) + else: + return self.items.pop() + return None + + def push(self, node): + self.items.append(node) + if self.type=='PRIO': + # Sorting reverse, because nodes with lowest cost/ value should be prioritized + self.items.sort(key = lambda item: item.value, reverse=True) \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..b578e8b --- /dev/null +++ b/main.py @@ -0,0 +1,73 @@ +from graph import Graph, Node, Queue +from utils import getNode + +# directed and weighted digraph +romania = Graph( ['Or', 'Ne', 'Ze', 'Ia', 'Ar', 'Si', 'Fa', + 'Va', 'Ri', 'Ti', 'Lu', 'Pi', 'Ur', 'Hi', + 'Me', 'Bu', 'Dr', 'Ef', 'Cr', 'Gi'], + [ + ('Or', 'Ze', 71), ('Or', 'Si', 151), + ('Ne', 'Ia', 87), ('Ze', 'Ar', 75), + ('Ia', 'Va', 92), ('Ar', 'Si', 140), + ('Ar', 'Ti', 118), ('Si', 'Fa', 99), + ('Si', 'Ri', 80), ('Fa', 'Bu', 211), + ('Va', 'Ur', 142), ('Ri', 'Pi', 97), + ('Ri', 'Cr', 146), ('Ti', 'Lu', 111), + ('Lu', 'Me', 70), ('Me', 'Dr', 75), + ('Dr', 'Cr', 120), ('Cr', 'Pi', 138), + ('Pi', 'Bu', 101), ('Bu', 'Gi', 90), + ('Bu', 'Ur', 85), ('Ur', 'Hi', 98), + ('Hi', 'Ef', 86) + ] ) + +def search(graph, queue, start_node_name, target_node_name): + cost = 0 + visited_nodes = [] # Nodes which have been visited + path = [] + + start_node = getNode(start_node_name, graph.nodes) + target_node = getNode(target_node_name, graph.nodes) + + queue.push(start_node) + + while not queue.empty(): + current_node = queue.pop() + visited_nodes.append(current_node.name) + + if current_node == target_node: + path.append(current_node.name) + tmp = current_node + while not tmp == start_node: + for edge in tmp.parent.edges: + if edge.end.name == tmp.name: + cost += edge.value + tmp = tmp.parent + path.insert(0, tmp.name) # reversed insertion + else: + for edge in current_node.edges: + neighbor = edge.end + + # works with digraph, because current_node is marked at this point, a cycle is not possible + if not visited_nodes.__contains__(neighbor.name): + queue.push(neighbor) + neighbor.parent = current_node + + if path.__len__() == 0: + print('zwischen ' + start_node_name + ' und ' + target_node_name + ' konnte kein Pfad gefunden werden') + else: + print('From ' + start_node_name + ' to ' + target_node_name + ': ') + print('Path: ' + path.__str__().format()) + print('Cost: ' + cost.__str__()) + +def bfs(graph, start_node_name, target_node_name): + search(graph,Queue('FIFO'),start_node_name, target_node_name) + +def dfs(graph, start_node_name, target_node_name): + search(graph,Queue('LIFO'),start_node_name, target_node_name) + +def main(): + bfs(romania, 'Ti', 'Bu') + dfs(romania, 'Ti', 'Bu') + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..784bb2e --- /dev/null +++ b/utils.py @@ -0,0 +1,2 @@ +def getNode(name, l): + return next(( i for i in l if i.name == name), -1 ) \ No newline at end of file