Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/directed/dfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,21 @@ where
type Item = N;

fn next(&mut self) -> Option<Self::Item> {
let n = self.to_see.pop()?;
if self.visited.contains(&n) {
return self.next();
}
self.visited.insert(n.clone());
let mut to_insert = Vec::new();
for s in (self.successors)(&n) {
if !self.visited.contains(&s) {
to_insert.push(s.clone());
loop {
let n = self.to_see.pop()?;
if self.visited.contains(&n) {
continue;
}
self.visited.insert(n.clone());
let mut to_insert = Vec::new();
for s in (self.successors)(&n) {
if !self.visited.contains(&s) {
to_insert.push(s.clone());
}
}
self.to_see.extend(to_insert.into_iter().rev());
return Some(n);
}
self.to_see.extend(to_insert.into_iter().rev());
Some(n)
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/directed/yen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ where
IN: IntoIterator<Item = (N, C)>,
FS: FnMut(&N) -> bool,
{
if k == 0 {
return vec![];
}
let Some((n, c)) = dijkstra_internal(start, &mut successors, &mut success) else {
return vec![];
};
Expand Down
17 changes: 17 additions & 0 deletions tests/dfs-reach.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@ fn issue_511_branches() {
let it = dfs_reach(0, |&n| [n + 2, n + 5].into_iter().filter(|&x| x <= 10));
assert_eq!(vec![0, 2, 4, 6, 8, 10, 9, 7, 5], it.collect::<Vec<_>>());
}

/// Test that `dfs_reach` does not stack overflow when many duplicate nodes
/// pile up in the `to_see` stack (would previously recurse for each duplicate).
#[test]
fn no_stack_overflow_with_duplicates() {
// Each node has N successors all pointing to the same next node, creating
// many duplicates in to_see. With the old recursive implementation, the
// recursion depth could equal the number of duplicates, causing stack overflow.
let n = 200_usize;
// Node 0 -> [1, 1, 1, ...] (n copies of 1)
// Node k -> [k+1, k+1, k+1, ...] (n copies of k+1) for k < n
// Node n -> []
let result: Vec<usize> =
dfs_reach(0usize, |&k| if k < n { vec![k + 1; n] } else { vec![] }).collect();
let expected: Vec<usize> = (0..=n).collect();
assert_eq!(result, expected);
}
16 changes: 16 additions & 0 deletions tests/yen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,19 @@ fn multiple_equal_cost_paths() {
assert_eq!(paths[0], (vec!['A', 'B', 'D'], 2));
assert_eq!(paths[1], (vec!['A', 'C', 'D'], 2));
}

/// Test that k=0 returns an empty result without panicking.
#[test]
fn k_zero() {
let result = yen(
&'c',
|c| match c {
'c' => vec![('d', 3)],
'd' => vec![],
_ => panic!(""),
},
|c| *c == 'd',
0,
);
assert!(result.is_empty());
}
Loading