Skip to content

Instantly share code, notes, and snippets.

@anikkon
Last active December 31, 2017 16:12
Show Gist options
  • Save anikkon/2792747c486f9343d1d497f7f04b4e8d to your computer and use it in GitHub Desktop.
Save anikkon/2792747c486f9343d1d497f7f04b4e8d to your computer and use it in GitHub Desktop.
from heapq import heappop, heappush
import itertools
class Node:
'Represents a vertex in an undirected weighted graph'
def __init__(self, index):
self.index = index
self.parent = None
self.visited = False
self.weight = 0
self.adj_list = []
class Graph:
'Represents undirected weighted graph'
def __init__(self, num_of_nodes):
self.nodes = [None for x in range(num_of_nodes)]
self.edges = {}
def add_node(self, node):
'Adds node to the list of nodes'
self.nodes[node.index] = node
def add_edge(self, start_node, end_node, distance):
'Adds edge to the list of edges'
start_node.adj_list.append(end_node)
end_node.adj_list.append(start_node)
self.edges[(start_node.index, end_node.index)] = distance
class PriorityQueue:
REMOVED = '<removed-node>' # placeholder for a removed node
def __init__(self):
self.queue = [] # list of nodes arranged in a heap
self.entry_finder = {} # mapping of nodes to entries
self.counter = itertools.count() # unique sequence count
def add_node(self, node):
'Add a new node or update the priority of an existing node'
self.remove_node(node)
count = next(self.counter)
entry = [-node.weight, count, node]
self.entry_finder[node] = entry
heappush(self.queue, entry)
def remove_node(self, node):
'"Removes" node from a priority queue. Marks as REMOVED'
try:
entry = self.entry_finder.pop(node)
entry[-1] = self.REMOVED
except KeyError:
pass
def pop_node(self):
'Remove and return the lowest priority node from a priority queue'
while self.queue:
_, _, node = heappop(self.queue)
if node is not self.REMOVED:
del self.entry_finder[node]
return node
def read_from_file(filename):
'Reads input data from file into graph'
with open(filename) as file:
num_of_nodes = int(file.readline().split()[0]) + 1
lines = file.readlines()
destination_node_index = int(lines.pop())
graph = Graph(num_of_nodes)
add_node = graph.add_node
add_edge = graph.add_edge
nodes = graph.nodes
for line in lines:
start_node_index, end_node_index, weight = line.split()
start_node = nodes[int(start_node_index)]
end_node = nodes[int(end_node_index)]
if start_node is None:
start_node = Node(int(start_node_index))
add_node(start_node)
if end_node is None:
end_node = Node(int(end_node_index))
add_node(end_node)
add_edge(start_node, end_node, int(weight))
return destination_node_index, graph
def graph_search(graph, source_node_index, destination_node_index):
'Traverses over the graph searching for a path with highest weight'
queue = PriorityQueue()
# local variables are faster
add_node = queue.add_node
pop_node = queue.pop_node
edges = graph.edges
nodes = graph.nodes
nodes[source_node_index].weight = float('inf')
for n in nodes:
if n is not None:
add_node(n)
while queue.queue:
start_node = pop_node()
if start_node.index == destination_node_index:
return start_node
for end_node in start_node.adj_list:
if end_node.visited:
continue
start_node_index = start_node.index
end_node_index = end_node.index
if start_node_index > end_node_index:
start_node_index, end_node_index = end_node_index, start_node_index
edge_weight = edges[(start_node_index, end_node_index)]
alt = min(start_node.weight, edge_weight)
if alt > end_node.weight:
end_node.weight = alt
end_node.parent = start_node
add_node(end_node)
start_node.visited = True
def print_path(node):
'Prints path from start node to end node recursively'
if node.parent:
print_path(node.parent)
print(" -> ", end="")
print(node.index, end="")
''' Algorithm to find a path with highest weight between 2 nodes in a weighted undirected graph.
Solution is a modified version of Dijkstra's algorithm.
Input data is read from a file, filename is passed as an argument.
Output is the maximum weight and the best path between the chosen nodes.
Nodes are indixed as positive integers. The start node is assumed to be 1.
Input file is expected to be in format:
"
number_of_nodes number_of_edges
...
node_1 node_2 edge_weight
...
destination_node
"
Run as:
# python <script name> <filename>
Estimated complexity:
O((E+V)*logV + E), V - number of vertices, E - number of edges
'''
import sys
from graph_search import *
start_index = 1
def main():
if len(sys.argv) is not 2:
print("Invalid arguments. Runt script as: \npython %s <filename>\n" % (__file__))
return
filename = sys.argv[1]
end_index, graph = read_from_file(filename)
end_node = graph_search(graph, start_index, end_index)
if end_node is None:
print("No path found")
return
print("MAX WEIGHT: ", end_node.weight)
print("PATH: ")
print_path(end_node)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment