patternpythonModerate
My attempt at Dijkstra's Algorithm in Python 3
Viewed 0 times
algorithmdijkstrapythonattempt
Problem
I am practicing my coding at HackerRank. The exact problem statement can be found here, but to summarize, it is finding the distances of the shortest path from a starting node to every other node in a non-directional graph where every edge is weighted 6. If a node is unreachable, the distance is -1. The nodes are named 1 to n. If you want an example, please see the provided one in the link.
To solve this, I googled an explanation of Dijkstra's Algorithm and tried my best to implement it (I am new to graph problems). My program passes 6/7 of the test cases, but it times out on the 7th. I improved some small inefficiencies, but it wasn't enough. Please advise me on how I may make the following faster:
```
def shortest (start, nodes, adj):
unique = [] #this part is pretty standard for this Algorithm I think
distance = [float('inf')]*nodes
for i in range(1, nodes+1):
if (len(adj[i-1])>0):
unique.append(i)
distance [start-1] = 0
while (len(unique)>0):
minDist = float('inf')
for i in unique:
if (distance[i-1] <= minDist):
minDist = distance[i-1]
distIndex = i
current = distIndex #sets the current node as the closest unvisited node
unique.remove(current)
for i in adj[current-1]: #updates the distance of each neighbour of the current node
neigh = i
temp = 6 + distance[current-1] #edges are 6
if temp < distance[neigh-1]:
distance[neigh-1] = temp
return distance
q = int(input()) #each test case contains multiple problems, so just consider one iteration
for i in range(q):
a = [] #for input purposes
b = [] #for output purposes
node = 0
edge = 0
#start of input
node, edge = list(map(int, input().strip().split())) #the input begins with the number of nodes and edges in the graph
for j in range(edge):
a.append(list(map(int, input().strip().spl
To solve this, I googled an explanation of Dijkstra's Algorithm and tried my best to implement it (I am new to graph problems). My program passes 6/7 of the test cases, but it times out on the 7th. I improved some small inefficiencies, but it wasn't enough. Please advise me on how I may make the following faster:
```
def shortest (start, nodes, adj):
unique = [] #this part is pretty standard for this Algorithm I think
distance = [float('inf')]*nodes
for i in range(1, nodes+1):
if (len(adj[i-1])>0):
unique.append(i)
distance [start-1] = 0
while (len(unique)>0):
minDist = float('inf')
for i in unique:
if (distance[i-1] <= minDist):
minDist = distance[i-1]
distIndex = i
current = distIndex #sets the current node as the closest unvisited node
unique.remove(current)
for i in adj[current-1]: #updates the distance of each neighbour of the current node
neigh = i
temp = 6 + distance[current-1] #edges are 6
if temp < distance[neigh-1]:
distance[neigh-1] = temp
return distance
q = int(input()) #each test case contains multiple problems, so just consider one iteration
for i in range(q):
a = [] #for input purposes
b = [] #for output purposes
node = 0
edge = 0
#start of input
node, edge = list(map(int, input().strip().split())) #the input begins with the number of nodes and edges in the graph
for j in range(edge):
a.append(list(map(int, input().strip().spl
Solution
Those comments are very difficult to read.
Format this properly:
Then remove fluff:
In this case just remove the first comment, because
Another pair is
Don't do this. Just call them
so you don't need the comment in the first place.
Then there's
Worse than that is when you outright lie. What would you expect the variable
And you write
so we expect the total number of nodes to be
But OK, fine. At least we know what
Nope, it actually means exactly the same thing as
A similar comment goes for
Then there's
Then there's
Going back to
but again this is a lie, because actually
So just write that.
For
If you write
you're misleading me into thinking you're going to use the index. When you're not going to, write
And what's up with that, followed by
followed by
Is
The middle can be
The comment
Isn't coherent. Try
Instead, though, don't put this information in
Don't call variables
You've double-indented the part where you update the neighbours.
Note that
is just
Why do you do this?
Just write
And the content can be
Do you not think this whole
Then this is better as just
Don't put spaces before the parentheses in function calls or indexing.
This code:
is just
and `cur
j = list(set(j)) #someone warned that the problematic test case gives the same edge many times, so this is to remove duplicatesFormat this properly:
# The problematic test case gives the same edge many times,
# so this is to remove duplicates
j = list(set(j))Then remove fluff:
# Remove duplicates. Helps some test cases.
j = list(set(j))In this case just remove the first comment, because
list(set(...)) is already known to mean deduplicate. And remove the second since it's implied by the fact you've written the code.j = list(set(j))Another pair is
a = [] # for input purposes
b = [] # for output purposesDon't do this. Just call them
descriptive_name_1 = []
descriptive_name_2 = []so you don't need the comment in the first place.
Then there's
q = .... So I wrack my brain thinking about what word contains q. But it turns out you just meant to write num_test_cases. What does that have to do with the letter q?Worse than that is when you outright lie. What would you expect the variable
node to contain? A node, right? Nope - it contains the total number of nodes.And you write
node = 0so we expect the total number of nodes to be
0 at that point, right? Nope, you immediately change your mind and writenode, edge = list(map(int, input().strip().split()))But OK, fine. At least we know what
nodes contains, then: multiple nodes, so a list (or other collection) of lengths of nodes.Nope, it actually means exactly the same thing as
node.A similar comment goes for
edge, which should be num_edges.Then there's
adj, which you label "adjacency matrix". Why not just call it adjacency_matrix then? But it's not even a dense matrix like that would imply, it's actually a list of per-node adjacencies, so call it adjacencies.Then there's
starting and start, which are the same thing named differently. Call them start_node or at least be consistent.Going back to
b, you haveb = []but again this is a lie, because actually
b = shortest(start_node, num_nodes, adjacencies)So just write that.
distances = shortest(start_node, num_nodes, adjacencies)For
a, initialize it as close to usage as possible and call it edges.If you write
for j in range(num_edges):you're misleading me into thinking you're going to use the index. When you're not going to, write
for _ in range(num_edges):And what's up with that, followed by
for j in edges:followed by
for j in adjacencies:Is
j just your general "this is a thing" variable? Most people use x, elem and item for that. Instead, writefor _ in range(num_edges):
for edge in edges:
for adjacent in adjacencies:The middle can be
for start, end in edges:The comment
#edges are 6Isn't coherent. Try
# Edges are length 6Instead, though, don't put this information in
shortest - use 1 and multiply out when using the results.Don't call variables
temp. It's a poor name.You've double-indented the part where you update the neighbours.
Note that
num_nodes, num_edges = list(map(int, input().strip().split()))is just
num_nodes, num_edges = map(int, input().strip().split())Why do you do this?
for i in adjacencies[current-1]:
neigh = iJust write
for neighbour in adjacencies[current-1]:And the content can be
for neighbour in adjacencies[current-1]:
distance[neighbour-1] = min(distance[neighbour-1], distance[current-1] + 1)Do you not think this whole
-1 thing is getting a bit silly? When reading, just decrement the edge values and starting edge correctly:edges = []
for _ in range(num_edges):
start, end = map(int, input().strip().split())
edges.append((start - 1, end - 1))
start_node = int(input()) - 1while loops and if statements don't need their condition parenthesized, and operators should be spaced properly:while (len(unique)>0): # before
while len(unique) > 0: # afterThen this is better as just
while unique.Don't put spaces before the parentheses in function calls or indexing.
minDist should be min_dist. You can also start with it set to num_nodes, and the same for distance's initialization.unique is a horrible name, and has nothing to do with the variable's purpose. Try unvisited instead. It can be initialized asunvisited = [i for i, adj in enumerate(adjacencies) if adj]distance, unsurprisingly, does not mean distance but instead distances. Fix that.This code:
min_dist = num_nodes
for i in unvisited:
if distances[i] <= min_dist:
min_dist = distances[i]
distIndex = i
current = distIndexis just
min_dist = num_nodes
for i in unvisited:
if distances[i] <= min_dist:
min_dist = distances[i]
current = iand `cur
Code Snippets
j = list(set(j)) #someone warned that the problematic test case gives the same edge many times, so this is to remove duplicates# The problematic test case gives the same edge many times,
# so this is to remove duplicates
j = list(set(j))# Remove duplicates. Helps some test cases.
j = list(set(j))j = list(set(j))a = [] # for input purposes
b = [] # for output purposesContext
StackExchange Code Review Q#138989, answer score: 12
Revisions (0)
No revisions yet.