|
3 | 3 | //! path exists and, if so, constructs and returns the path. |
4 | 4 |
|
5 | 5 | use std::collections::LinkedList; |
6 | | -use std::vec::Vec; |
7 | 6 |
|
8 | | -/// Finds the Eulerian path in a directed graph represented by the number of nodes and edges. |
| 7 | +/// Finds an Eulerian path in a directed graph. |
9 | 8 | /// |
10 | 9 | /// # Arguments |
11 | 10 | /// |
12 | | -/// * `nodes` - The number of nodes in the graph. |
13 | | -/// * `edges` - A vector of tuples representing the directed edges in the graph, where each tuple |
14 | | -/// is of the form `(u, v)` indicating a directed edge from `u` to `v`. |
15 | | -/// |
16 | | -/// The function checks if an Eulerian path exists and, if so, constructs and returns one valid path. |
| 11 | +/// * `node_count` - The number of nodes in the graph. |
| 12 | +/// * `edge_list` - A vector of tuples representing directed edges, where each tuple is of the form `(start, end)`. |
17 | 13 | /// |
18 | 14 | /// # Returns |
19 | 15 | /// |
20 | | -/// An `Option` containing a vector representing the Eulerian path if it exists, or `None` if no such path exists. |
21 | | -pub fn find_eulerian_path(nodes: usize, edges: Vec<(usize, usize)>) -> Option<Vec<usize>> { |
22 | | - let mut graph = vec![Vec::new(); nodes]; |
23 | | - for (u, v) in edges { |
24 | | - graph[u].push(v); |
| 16 | +/// An `Option<Vec<usize>>` containing the Eulerian path if it exists; otherwise, `None`. |
| 17 | +pub fn find_eulerian_path(node_count: usize, edge_list: Vec<(usize, usize)>) -> Option<Vec<usize>> { |
| 18 | + let mut adjacency_list = vec![Vec::new(); node_count]; |
| 19 | + for (start, end) in edge_list { |
| 20 | + adjacency_list[start].push(end); |
25 | 21 | } |
26 | 22 |
|
27 | | - let mut solver = EulerianPath::new(graph); |
28 | | - solver.find_eulerian_path() |
| 23 | + let mut eulerian_solver = EulerianPathSolver::new(adjacency_list); |
| 24 | + eulerian_solver.find_path() |
29 | 25 | } |
30 | 26 |
|
31 | | -/// Struct representing an Eulerian path in a directed graph. |
32 | | -pub struct EulerianPath { |
33 | | - nodes: usize, // Number of nodes |
34 | | - edges: usize, // Total number of edges |
35 | | - in_deg: Vec<usize>, // In-degrees of nodes |
36 | | - out_deg: Vec<usize>, // Out-degrees of nodes |
37 | | - path: LinkedList<usize>, // Stores the Eulerian path |
38 | | - graph: Vec<Vec<usize>>, // Adjacency list |
| 27 | +/// Struct to represent the solver for finding an Eulerian path in a directed graph. |
| 28 | +pub struct EulerianPathSolver { |
| 29 | + node_count: usize, |
| 30 | + edge_count: usize, |
| 31 | + in_degrees: Vec<usize>, |
| 32 | + out_degrees: Vec<usize>, |
| 33 | + eulerian_path: LinkedList<usize>, |
| 34 | + adjacency_list: Vec<Vec<usize>>, |
39 | 35 | } |
40 | 36 |
|
41 | | -impl EulerianPath { |
42 | | - /// Creates a new instance of `EulerianPath` for the given graph. |
| 37 | +impl EulerianPathSolver { |
| 38 | + /// Creates a new instance of `EulerianPathSolver`. |
43 | 39 | /// |
44 | 40 | /// # Arguments |
45 | 41 | /// |
46 | | - /// * `graph` - A directed graph represented as an adjacency list. |
| 42 | + /// * `adjacency_list` - The graph represented as an adjacency list. |
47 | 43 | /// |
48 | 44 | /// # Returns |
49 | 45 | /// |
50 | | - /// A new `EulerianPath` instance. |
51 | | - pub fn new(graph: Vec<Vec<usize>>) -> Self { |
| 46 | + /// A new instance of `EulerianPathSolver`. |
| 47 | + pub fn new(adjacency_list: Vec<Vec<usize>>) -> Self { |
52 | 48 | Self { |
53 | | - nodes: graph.len(), |
54 | | - edges: 0, |
55 | | - in_deg: vec![0; graph.len()], |
56 | | - out_deg: vec![0; graph.len()], |
57 | | - path: LinkedList::new(), |
58 | | - graph, |
| 49 | + node_count: adjacency_list.len(), |
| 50 | + edge_count: 0, |
| 51 | + in_degrees: vec![0; adjacency_list.len()], |
| 52 | + out_degrees: vec![0; adjacency_list.len()], |
| 53 | + eulerian_path: LinkedList::new(), |
| 54 | + adjacency_list, |
59 | 55 | } |
60 | 56 | } |
61 | 57 |
|
62 | | - /// Finds an Eulerian path if it exists. |
| 58 | + /// Computes the Eulerian path if it exists. |
63 | 59 | /// |
64 | 60 | /// # Returns |
65 | 61 | /// |
66 | | - /// An `Option` containing the Eulerian path as a vector if it exists, or `None` otherwise. |
67 | | - fn find_eulerian_path(&mut self) -> Option<Vec<usize>> { |
68 | | - self.init_degrees(); |
| 62 | + /// An `Option<Vec<usize>>` containing the Eulerian path if found; otherwise, `None`. |
| 63 | + /// If multiple Eulerian paths exist, the one found will be returned, but it may not be unique. |
| 64 | + fn find_path(&mut self) -> Option<Vec<usize>> { |
| 65 | + self.initialize_degrees(); |
69 | 66 |
|
70 | 67 | if !self.has_eulerian_path() { |
71 | 68 | return None; |
72 | 69 | } |
73 | 70 |
|
74 | | - let start = self.find_start(); |
75 | | - self.dfs(start); |
| 71 | + let start_node = self.get_start_node(); |
| 72 | + self.depth_first_search(start_node); |
76 | 73 |
|
77 | | - if self.path.len() != self.edges + 1 { |
| 74 | + if self.eulerian_path.len() != self.edge_count + 1 { |
78 | 75 | return None; |
79 | 76 | } |
80 | 77 |
|
81 | | - let mut solution = Vec::with_capacity(self.edges + 1); |
82 | | - while let Some(node) = self.path.pop_front() { |
83 | | - solution.push(node); |
| 78 | + let mut path = Vec::with_capacity(self.edge_count + 1); |
| 79 | + while let Some(node) = self.eulerian_path.pop_front() { |
| 80 | + path.push(node); |
84 | 81 | } |
85 | 82 |
|
86 | | - Some(solution) |
| 83 | + Some(path) |
87 | 84 | } |
88 | 85 |
|
89 | | - /// Initializes in-degrees, out-degrees, and counts the total number of edges. |
90 | | - fn init_degrees(&mut self) { |
91 | | - for (u, neighbors) in self.graph.iter().enumerate() { |
92 | | - for &v in neighbors { |
93 | | - self.in_deg[v] += 1; |
94 | | - self.out_deg[u] += 1; |
95 | | - self.edges += 1; |
| 86 | + /// Initializes in-degrees and out-degrees for each node and counts total edges. |
| 87 | + fn initialize_degrees(&mut self) { |
| 88 | + for (start_node, neighbors) in self.adjacency_list.iter().enumerate() { |
| 89 | + for &end_node in neighbors { |
| 90 | + self.in_degrees[end_node] += 1; |
| 91 | + self.out_degrees[start_node] += 1; |
| 92 | + self.edge_count += 1; |
96 | 93 | } |
97 | 94 | } |
98 | 95 | } |
99 | 96 |
|
100 | | - /// Checks if the graph has an Eulerian path. |
| 97 | + /// Checks if an Eulerian path is possible in the graph. |
101 | 98 | /// |
102 | 99 | /// # Returns |
103 | 100 | /// |
104 | | - /// `true` if an Eulerian path exists, `false` otherwise. |
| 101 | + /// `true` if an Eulerian path exists; otherwise, `false`. |
105 | 102 | fn has_eulerian_path(&self) -> bool { |
106 | | - if self.edges == 0 { |
| 103 | + if self.edge_count == 0 { |
107 | 104 | return false; |
108 | 105 | } |
109 | 106 |
|
110 | | - let (mut start, mut end) = (0, 0); |
111 | | - for i in 0..self.nodes { |
112 | | - let (in_deg, out_deg) = (self.in_deg[i] as isize, self.out_deg[i] as isize); |
113 | | - match out_deg - in_deg { |
114 | | - 1 => start += 1, |
115 | | - -1 => end += 1, |
116 | | - d if d.abs() > 1 => return false, |
| 107 | + let (mut start_nodes, mut end_nodes) = (0, 0); |
| 108 | + for i in 0..self.node_count { |
| 109 | + let (in_degree, out_degree) = |
| 110 | + (self.in_degrees[i] as isize, self.out_degrees[i] as isize); |
| 111 | + match out_degree - in_degree { |
| 112 | + 1 => start_nodes += 1, |
| 113 | + -1 => end_nodes += 1, |
| 114 | + degree_diff if degree_diff.abs() > 1 => return false, |
117 | 115 | _ => (), |
118 | 116 | } |
119 | 117 | } |
120 | 118 |
|
121 | | - (start == 0 && end == 0) || (start == 1 && end == 1) |
| 119 | + (start_nodes == 0 && end_nodes == 0) || (start_nodes == 1 && end_nodes == 1) |
122 | 120 | } |
123 | 121 |
|
124 | | - /// Finds the start node for the Eulerian path. |
| 122 | + /// Finds the starting node for the Eulerian path. |
125 | 123 | /// |
126 | 124 | /// # Returns |
127 | 125 | /// |
128 | | - /// The index of the start node. |
129 | | - fn find_start(&self) -> usize { |
130 | | - for i in 0..self.nodes { |
131 | | - if self.out_deg[i] > self.in_deg[i] { |
| 126 | + /// The index of the starting node. |
| 127 | + fn get_start_node(&self) -> usize { |
| 128 | + for i in 0..self.node_count { |
| 129 | + if self.out_degrees[i] > self.in_degrees[i] { |
132 | 130 | return i; |
133 | 131 | } |
134 | 132 | } |
135 | | - (0..self.nodes).find(|&i| self.out_deg[i] > 0).unwrap_or(0) |
| 133 | + (0..self.node_count) |
| 134 | + .find(|&i| self.out_degrees[i] > 0) |
| 135 | + .unwrap_or(0) |
136 | 136 | } |
137 | 137 |
|
138 | | - /// Depth-first search traversal to construct the Eulerian path. |
| 138 | + /// Performs depth-first search to construct the Eulerian path. |
139 | 139 | /// |
140 | 140 | /// # Arguments |
141 | 141 | /// |
142 | | - /// * `u` - The current node being traversed. |
143 | | - fn dfs(&mut self, u: usize) { |
144 | | - while self.out_deg[u] > 0 { |
145 | | - let v = self.graph[u][self.out_deg[u] - 1]; |
146 | | - self.out_deg[u] -= 1; |
147 | | - self.dfs(v); |
| 142 | + /// * `curr_node` - The current node being visited in the DFS traversal. |
| 143 | + fn depth_first_search(&mut self, curr_node: usize) { |
| 144 | + while self.out_degrees[curr_node] > 0 { |
| 145 | + let next_node = self.adjacency_list[curr_node][self.out_degrees[curr_node] - 1]; |
| 146 | + self.out_degrees[curr_node] -= 1; |
| 147 | + self.depth_first_search(next_node); |
148 | 148 | } |
149 | | - self.path.push_front(u); |
| 149 | + self.eulerian_path.push_front(curr_node); |
150 | 150 | } |
151 | 151 | } |
152 | 152 |
|
|
0 commit comments