|
1 | 1 | use std::collections::VecDeque; |
2 | 2 |
|
3 | 3 | pub fn run() { |
4 | | - println!("\nLesson: BFS on grid"); |
| 4 | + println!("\nDay 155 - BFS on grid: shortest route reconstruction"); |
5 | 5 |
|
6 | 6 | let grid = vec![ |
7 | | - vec![0, 0, 1], |
8 | | - vec![1, 0, 0], |
9 | | - vec![1, 0, 0], |
| 7 | + vec![0, 0, 1, 0], |
| 8 | + vec![1, 0, 1, 0], |
| 9 | + vec![1, 0, 0, 0], |
| 10 | + vec![1, 1, 1, 0], |
10 | 11 | ]; |
11 | | - println!("distance={:?}", bfs((0, 0), (2, 2), &grid)); |
| 12 | + |
| 13 | + let route = shortest_path((0, 0), (3, 3), &grid); |
| 14 | + println!("route={route:?}"); |
12 | 15 | } |
13 | 16 |
|
14 | | -fn bfs(start: (usize, usize), target: (usize, usize), grid: &[Vec<i32>]) -> Option<i32> { |
15 | | - let n = grid.len(); |
16 | | - let m = grid[0].len(); |
17 | | - let mut dist = vec![vec![-1; m]; n]; |
18 | | - let mut q = VecDeque::new(); |
| 17 | +pub fn shortest_path( |
| 18 | + start: (usize, usize), |
| 19 | + target: (usize, usize), |
| 20 | + grid: &[Vec<i32>], |
| 21 | +) -> Option<Vec<(usize, usize)>> { |
| 22 | + if grid.is_empty() || grid[0].is_empty() { |
| 23 | + return None; |
| 24 | + } |
| 25 | + if grid[start.0][start.1] == 1 || grid[target.0][target.1] == 1 { |
| 26 | + return None; |
| 27 | + } |
| 28 | + |
| 29 | + let rows = grid.len(); |
| 30 | + let cols = grid[0].len(); |
| 31 | + let mut queue = VecDeque::new(); |
| 32 | + let mut visited = vec![vec![false; cols]; rows]; |
| 33 | + let mut prev = vec![vec![None; cols]; rows]; |
| 34 | + |
| 35 | + visited[start.0][start.1] = true; |
| 36 | + queue.push_back(start); |
| 37 | + |
| 38 | + let directions = [(1_i32, 0_i32), (-1, 0), (0, 1), (0, -1)]; |
19 | 39 |
|
20 | | - q.push_back(start); |
21 | | - dist[start.0][start.1] = 0; |
| 40 | + while let Some((x, y)) = queue.pop_front() { |
| 41 | + if (x, y) == target { |
| 42 | + return Some(reconstruct_path(start, target, &prev)); |
| 43 | + } |
22 | 44 |
|
23 | | - let dirs = [(1,0),(-1,0),(0,1),(0,-1)]; |
24 | | - while let Some((x,y)) = q.pop_front() { |
25 | | - if (x,y) == target { return Some(dist[x][y]); } |
26 | | - for (dx,dy) in dirs { |
| 45 | + for (dx, dy) in directions { |
27 | 46 | let nx = x as i32 + dx; |
28 | 47 | let ny = y as i32 + dy; |
29 | | - if nx < 0 || ny < 0 || nx >= n as i32 || ny >= m as i32 { continue; } |
30 | | - let (nx,ny) = (nx as usize, ny as usize); |
31 | | - if grid[nx][ny] == 1 || dist[nx][ny] != -1 { continue; } |
32 | | - dist[nx][ny] = dist[x][y] + 1; |
33 | | - q.push_back((nx,ny)); |
| 48 | + if nx < 0 || ny < 0 || nx >= rows as i32 || ny >= cols as i32 { |
| 49 | + continue; |
| 50 | + } |
| 51 | + |
| 52 | + let (nx, ny) = (nx as usize, ny as usize); |
| 53 | + if grid[nx][ny] == 1 || visited[nx][ny] { |
| 54 | + continue; |
| 55 | + } |
| 56 | + |
| 57 | + visited[nx][ny] = true; |
| 58 | + prev[nx][ny] = Some((x, y)); |
| 59 | + queue.push_back((nx, ny)); |
34 | 60 | } |
35 | 61 | } |
| 62 | + |
36 | 63 | None |
37 | 64 | } |
| 65 | + |
| 66 | +fn reconstruct_path( |
| 67 | + start: (usize, usize), |
| 68 | + target: (usize, usize), |
| 69 | + prev: &[Vec<Option<(usize, usize)>>], |
| 70 | +) -> Vec<(usize, usize)> { |
| 71 | + let mut path = Vec::new(); |
| 72 | + let mut current = Some(target); |
| 73 | + |
| 74 | + while let Some(node) = current { |
| 75 | + path.push(node); |
| 76 | + if node == start { |
| 77 | + break; |
| 78 | + } |
| 79 | + current = prev[node.0][node.1]; |
| 80 | + } |
| 81 | + |
| 82 | + path.reverse(); |
| 83 | + path |
| 84 | +} |
| 85 | + |
| 86 | +#[cfg(test)] |
| 87 | +mod tests { |
| 88 | + use super::shortest_path; |
| 89 | + |
| 90 | + #[test] |
| 91 | + fn reconstructs_shortest_grid_path() { |
| 92 | + let grid = vec![ |
| 93 | + vec![0, 0, 1], |
| 94 | + vec![1, 0, 0], |
| 95 | + vec![1, 0, 0], |
| 96 | + ]; |
| 97 | + |
| 98 | + let path = shortest_path((0, 0), (2, 2), &grid).expect("path exists"); |
| 99 | + assert_eq!(path.first(), Some(&(0, 0))); |
| 100 | + assert_eq!(path.last(), Some(&(2, 2))); |
| 101 | + assert_eq!(path.len(), 5); |
| 102 | + } |
| 103 | + |
| 104 | + #[test] |
| 105 | + fn returns_none_for_blocked_target() { |
| 106 | + let grid = vec![vec![0, 1], vec![1, 1]]; |
| 107 | + assert_eq!(shortest_path((0, 0), (1, 1), &grid), None); |
| 108 | + } |
| 109 | +} |
0 commit comments