Skip to content

Commit e194a4c

Browse files
authored
Merge pull request #2740 from keon/fix/bfs-deque-bellman-heap
Fix BFS O(n²) regression, bellman_ford sink nodes, heap bugs
2 parents bd4980a + ae4e168 commit e194a4c

10 files changed

Lines changed: 41 additions & 20 deletions

File tree

algorithms/data_structures/heap.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ def perc_up(self, index: int) -> None:
9595
self.heap[index // 2],
9696
self.heap[index],
9797
)
98-
index = index // 2
98+
index = index // 2
99+
else:
100+
break
99101

100102
def insert(self, val: int) -> None:
101103
"""Insert a value into the heap.
@@ -128,7 +130,7 @@ def perc_down(self, index: int) -> None:
128130
Args:
129131
index: Index of the element to percolate down.
130132
"""
131-
while 2 * index < self.current_size:
133+
while 2 * index <= self.current_size:
132134
smaller_child = self.min_child(index)
133135
if self.heap[smaller_child] < self.heap[index]:
134136
self.heap[smaller_child], self.heap[index] = (

algorithms/graph/bellman_ford.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ def _initialize_single_source(
6969
distance: Dictionary to store shortest distances (modified in place).
7070
predecessor: Dictionary to store path predecessors (modified in place).
7171
"""
72-
for node in graph:
72+
all_nodes: set[str] = set(graph.keys())
73+
for neighbors in graph.values():
74+
all_nodes.update(neighbors.keys())
75+
for node in all_nodes:
7376
distance[node] = float("inf")
7477
predecessor[node] = None
7578
distance[source] = 0

algorithms/graph/check_bipartite.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
from __future__ import annotations
1414

15+
from collections import deque
16+
1517

1618
def check_bipartite(adj_list: list[list[int]]) -> bool:
1719
"""Return True if the graph represented by *adj_list* is bipartite.
@@ -32,10 +34,10 @@ def check_bipartite(adj_list: list[list[int]]) -> bool:
3234
set_type = [-1 for _ in range(vertices)]
3335
set_type[0] = 0
3436

35-
queue = [0]
37+
queue = deque([0])
3638

3739
while queue:
38-
current = queue.pop(0)
40+
current = queue.popleft()
3941

4042
if adj_list[current][current]:
4143
return False

algorithms/graph/count_islands_bfs.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
from __future__ import annotations
1616

17+
from collections import deque
18+
1719

1820
def count_islands(grid: list[list[int]]) -> int:
1921
"""Return the number of islands in *grid*.
@@ -34,15 +36,15 @@ def count_islands(grid: list[list[int]]) -> int:
3436
num_islands = 0
3537
visited = [[0] * col for _ in range(row)]
3638
directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
37-
queue: list[tuple[int, int]] = []
39+
queue: deque[tuple[int, int]] = deque()
3840

3941
for i in range(row):
4042
for j, num in enumerate(grid[i]):
4143
if num == 1 and visited[i][j] != 1:
4244
visited[i][j] = 1
4345
queue.append((i, j))
4446
while queue:
45-
x, y = queue.pop(0)
47+
x, y = queue.popleft()
4648
for k in range(len(directions)):
4749
nx_x = x + directions[k][0]
4850
nx_y = y + directions[k][1]

algorithms/graph/shortest_distance_from_all_buildings.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from __future__ import annotations
1515

16+
from collections import deque
17+
1618

1719
def shortest_distance(grid: list[list[int]]) -> int:
1820
"""Return the minimum total distance from an empty cell to all buildings.
@@ -64,9 +66,9 @@ def _bfs(
6466
j: Column of the building.
6567
count: Number of buildings visited so far.
6668
"""
67-
q: list[tuple[int, int, int]] = [(i, j, 0)]
69+
q: deque[tuple[int, int, int]] = deque([(i, j, 0)])
6870
while q:
69-
i, j, step = q.pop(0)
71+
i, j, step = q.popleft()
7072
for k, col in [(i - 1, j), (i + 1, j), (i, j - 1), (i, j + 1)]:
7173
if (
7274
0 <= k < len(grid)

algorithms/graph/traversal.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from __future__ import annotations
1313

14+
from collections import deque
1415
from typing import Any
1516

1617

@@ -55,9 +56,9 @@ def bfs_traverse(graph: dict[Any, list[Any]], start: Any) -> set[Any]:
5556
['a', 'b']
5657
"""
5758
visited: set[Any] = set()
58-
queue = [start]
59+
queue = deque([start])
5960
while queue:
60-
node = queue.pop(0)
61+
node = queue.popleft()
6162
if node not in visited:
6263
visited.add(node)
6364
for next_node in graph[node]:

algorithms/queue/zigzagiterator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from __future__ import annotations
1515

16+
from collections import deque
17+
1618

1719
class ZigZagIterator:
1820
"""Iterator that interleaves elements from two lists.
@@ -32,15 +34,15 @@ def __init__(self, v1: list[int], v2: list[int]) -> None:
3234
v1: First input list.
3335
v2: Second input list.
3436
"""
35-
self.queue: list[list[int]] = [lst for lst in (v1, v2) if lst]
37+
self.queue: deque[list[int]] = deque(lst for lst in (v1, v2) if lst)
3638

3739
def next(self) -> int:
3840
"""Return the next element in zigzag order.
3941
4042
Returns:
4143
The next interleaved element.
4244
"""
43-
current_list = self.queue.pop(0)
45+
current_list = self.queue.popleft()
4446
ret = current_list.pop(0)
4547
if current_list:
4648
self.queue.append(current_list)

algorithms/tree/max_height.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from __future__ import annotations
1515

16+
from collections import deque
17+
1618
from algorithms.tree.tree import TreeNode
1719

1820

@@ -32,12 +34,12 @@ def max_height(root: TreeNode | None) -> int:
3234
if root is None:
3335
return 0
3436
height = 0
35-
queue: list[TreeNode] = [root]
37+
queue: deque[TreeNode] = deque([root])
3638
while queue:
3739
height += 1
38-
level: list[TreeNode] = []
40+
level: deque[TreeNode] = deque()
3941
while queue:
40-
node = queue.pop(0)
42+
node = queue.popleft()
4143
if node.left is not None:
4244
level.append(node.left)
4345
if node.right is not None:

algorithms/tree/path_sum.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
from __future__ import annotations
1616

17+
from collections import deque
18+
1719
from algorithms.tree.tree import TreeNode
1820

1921

@@ -83,9 +85,9 @@ def has_path_sum3(root: TreeNode | None, sum: int) -> bool:
8385
"""
8486
if root is None:
8587
return False
86-
queue: list[tuple[TreeNode, int]] = [(root, sum - root.val)]
88+
queue: deque[tuple[TreeNode, int]] = deque([(root, sum - root.val)])
8789
while queue:
88-
node, val = queue.pop(0)
90+
node, val = queue.popleft()
8991
if node.left is None and node.right is None and val == 0:
9092
return True
9193
if node.left is not None:

algorithms/tree/path_sum2.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from __future__ import annotations
1515

16+
from collections import deque
17+
1618
from algorithms.tree.tree import TreeNode
1719

1820

@@ -101,9 +103,10 @@ def path_sum3(root: TreeNode | None, sum: int) -> list[list[int]]:
101103
if root is None:
102104
return []
103105
result: list[list[int]] = []
104-
queue: list[tuple[TreeNode, int, list[int]]] = [(root, root.val, [root.val])]
106+
initial = (root, root.val, [root.val])
107+
queue: deque[tuple[TreeNode, int, list[int]]] = deque([initial])
105108
while queue:
106-
node, val, path = queue.pop(0)
109+
node, val, path = queue.popleft()
107110
if node.left is None and node.right is None and val == sum:
108111
result.append(path)
109112
if node.left is not None:

0 commit comments

Comments
 (0)