Update BFS impls

I've subtly been implementing breadth-first traversals in graphs
incorrectly. The change is subtle, but updating `seen` needs to happen
immediately after queuing an item.

The results will remain the same, but the runtimes will differ dramatically. I
didn't notice this until I attempted to complete LeetCode's "count islands"
challenge, and LeetCode rejected my solution because it could not finish before
timing out. After looking at other candidates' solutions and comparing them to
mine, I couldn't see any difference... except for this subtle difference.

This SO answer provides a helpful explanation:
https://stackoverflow.com/questions/45623722/marking-node-as-visited-on-bfs-when-dequeuing

The take-away lesson here is to always call `seen.add(..)` immediately after
enqueuing.
This commit is contained in:
William Carroll 2020-11-23 23:21:20 +00:00
parent c00eed469c
commit 9549dbb266
2 changed files with 3 additions and 3 deletions

View file

@ -8,6 +8,7 @@ def maybe_queue(row, col, game, q, seen):
if row >= 0 and row < len(game) and col >= 0 and col < len(game[0]): if row >= 0 and row < len(game) and col >= 0 and col < len(game[0]):
if game[row][col] == 'L' and (row, col) not in seen: if game[row][col] == 'L' and (row, col) not in seen:
q.append((row, col)) q.append((row, col))
seen.add((row, col))
def visit_island(row, col, game, seen): def visit_island(row, col, game, seen):
""" """
@ -18,7 +19,6 @@ def visit_island(row, col, game, seen):
q.append((row, col)) q.append((row, col))
while q: while q:
row, col = q.popleft() row, col = q.popleft()
seen.add((row, col))
maybe_queue(row - 1, col, game, q, seen) # UP maybe_queue(row - 1, col, game, q, seen) # UP
maybe_queue(row + 1, col, game, q, seen) # DOWN maybe_queue(row + 1, col, game, q, seen) # DOWN
maybe_queue(row, col - 1, game, q, seen) # LEFT maybe_queue(row, col - 1, game, q, seen) # LEFT

View file

@ -25,10 +25,10 @@ class GraphNode(object):
while xs: while xs:
node = xs.popleft() node = xs.popleft()
result.append('{} ({})'.format(node.label, str(node.color))) result.append('{} ({})'.format(node.label, str(node.color)))
seen.add(node.label)
for c in node.neighbors: for c in node.neighbors:
if c.label not in seen: if c.label not in seen:
xs.append(c) xs.append(c)
seen.add(node.label)
return ', '.join(result) return ', '.join(result)
def color_graph(graph, d): def color_graph(graph, d):
@ -40,11 +40,11 @@ def color_graph(graph, d):
while xs: while xs:
x, color = xs.popleft() x, color = xs.popleft()
x.color = color x.color = color
seen.add(x.label)
for c in x.neighbors: for c in x.neighbors:
if c.label not in seen: if c.label not in seen:
palette.advance() palette.advance()
xs.append((c, palette.get())) xs.append((c, palette.get()))
seen.add(x.label)
a = GraphNode('a') a = GraphNode('a')
b = GraphNode('b') b = GraphNode('b')