Skip to content

Commit 0834bd1

Browse files
committed
Faster double queue
1 parent e64181e commit 0834bd1

File tree

2 files changed

+20
-24
lines changed

2 files changed

+20
-24
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
8787
| 13 | [Claw Contraption](https://adventofcode.com/2024/day/13) | [Source](src/year2024/day13.rs) | 14 |
8888
| 14 | [Restroom Redoubt](https://adventofcode.com/2024/day/14) | [Source](src/year2024/day14.rs) | 74 |
8989
| 15 | [Warehouse Woes](https://adventofcode.com/2024/day/15) | [Source](src/year2024/day15.rs) | 303 |
90-
| 16 | [Reindeer Maze](https://adventofcode.com/2024/day/16) | [Source](src/year2024/day16.rs) | 422 |
90+
| 16 | [Reindeer Maze](https://adventofcode.com/2024/day/16) | [Source](src/year2024/day16.rs) | 390 |
9191
| 17 | [Chronospatial Computer](https://adventofcode.com/2024/day/17) | [Source](src/year2024/day17.rs) | 6 |
9292

9393
## 2023

src/year2024/day16.rs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,6 @@
88
//! Part two is a a BFS *backwards* from the end to the finish, tracing the cost exactly
99
//! to find all possible paths. This reuses the cost information from the Dijkstra without
1010
//! requiring any extra state keeping for the paths.
11-
//!
12-
//! To speed things up even further we use a trick. Classic Dijkstra uses a generic priority queue
13-
//! that can be implemented in Rust using a [`BinaryHeap`]. However the total cost follows a
14-
//! strictly increasing order in a constrained range of values, so we can use a much faster
15-
//! [bucket queue](https://en.wikipedia.org/wiki/Bucket_queue). The maximum possible increase is
16-
//! 1000 so we need 1001 buckets.
17-
//!
18-
//! [`BinaryHeap`]: std::collections::BinaryHeap
1911
use crate::util::grid::*;
2012
use crate::util::point::*;
2113
use std::collections::VecDeque;
@@ -30,25 +22,26 @@ pub fn parse(input: &str) -> Input {
3022
let start = grid.find(b'S').unwrap();
3123
let end = grid.find(b'E').unwrap();
3224

33-
// Forwards Dijkstra
34-
let mut buckets = vec![Vec::new(); 1001];
25+
// Forwards Dijkstra. Since turns are so much more expensive than moving forward, we can
26+
// treat this as a glorified BFS using two priority queues. This is much faster than using
27+
// an actual min heap.
28+
let mut todo_first = VecDeque::new();
29+
let mut todo_second = VecDeque::new();
3530
// State is `(position, direction)`.
3631
let mut seen = grid.same_size_with([u32::MAX; 4]);
37-
let mut cost = 0;
3832
let mut lowest = u32::MAX;
3933

40-
buckets[0].push((start, 0));
34+
todo_first.push_back((start, 0, 0));
4135
seen[start][0] = 0;
4236

43-
while lowest == u32::MAX {
44-
let index = (cost % 1001) as usize;
45-
46-
while let Some((position, direction)) = buckets[index].pop() {
47-
// Once we find the end node then stop. All paths of the same cost must be in
48-
// this bucket, so have already been accounted for.
37+
while !todo_first.is_empty() {
38+
while let Some((position, direction, cost)) = todo_first.pop_front() {
39+
if cost >= lowest {
40+
continue;
41+
}
4942
if position == end {
5043
lowest = cost;
51-
break;
44+
continue;
5245
}
5346

5447
// -1.rem_euclid(4) = 3
@@ -60,17 +53,20 @@ pub fn parse(input: &str) -> Input {
6053
(position, right, cost + 1000),
6154
];
6255

63-
for (next_position, next_direction, next_cost) in next {
56+
for tuple @ (next_position, next_direction, next_cost) in next {
6457
if grid[next_position] != b'#' && next_cost < seen[next_position][next_direction] {
6558
// Find the next bucket.
66-
let index = (next_cost % 1001) as usize;
67-
buckets[index].push((next_position, next_direction));
59+
if next_direction == direction {
60+
todo_first.push_back(tuple);
61+
} else {
62+
todo_second.push_back(tuple);
63+
}
6864
seen[next_position][next_direction] = next_cost;
6965
}
7066
}
7167
}
7268

73-
cost += 1;
69+
(todo_first, todo_second) = (todo_second, todo_first);
7470
}
7571

7672
// Backwards BFS

0 commit comments

Comments
 (0)