Skip to content

Commit 559b865

Browse files
committed
feat: refactor implementation
- Move error handling to appropriate methods - Add `num_vertices` method
1 parent 7dcef78 commit 559b865

File tree

1 file changed

+63
-53
lines changed

1 file changed

+63
-53
lines changed

src/backtracking/hamiltonian_cycle.rs

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,58 @@
11
//! This module provides functionality to find a Hamiltonian cycle in a graph.
22
//! Source: [Uncyclopedia](https://en.wikipedia.org/wiki/Hamiltonian_path_problem)
33
4-
/// Represents errors that can occur while working with an adjacency matrix.
4+
/// Represents potential errors when working with an adjacency matrix.
55
#[derive(Debug, PartialEq, Eq)]
66
pub enum AdjMatError {
7-
/// The adjacency matrix is empty.
7+
/// Indicates that the adjacency matrix is empty.
88
EmptyMat,
9-
/// The adjacency matrix is not square.
9+
/// Indicates that the adjacency matrix is not square.
1010
ImproperMat,
11-
/// The start vertex is out of bounds.
11+
/// Indicates that the starting vertex is out of bounds.
1212
StartOutOfBound,
1313
}
1414

15-
/// Represents a graph with an adjacency matrix.
15+
/// Represents a graph using an adjacency matrix.
1616
struct Graph {
1717
/// The adjacency matrix representing the graph.
1818
adjacency_matrix: Vec<Vec<bool>>,
1919
}
2020

2121
impl Graph {
22-
/// Creates a new graph with the given adjacency matrix.
22+
/// Creates a new graph with the provided adjacency matrix.
2323
///
2424
/// # Arguments
2525
///
26-
/// * `adjacency_matrix` - A square matrix where each element represents
27-
/// the presence `true` or absence `false` of an edge
26+
/// * `adjacency_matrix` - A square matrix where each element indicates
27+
/// the presence (`true`) or absence (`false`) of an edge
2828
/// between two vertices.
2929
///
3030
/// # Returns
3131
///
32-
/// A graph that represents adjacency matrix
33-
fn new(adjacency_matrix: Vec<Vec<bool>>) -> Self {
34-
Self { adjacency_matrix }
32+
/// A `Result` containing the graph if successful, or an `AdjMatError` if there is an issue with the matrix.
33+
fn new(adjacency_matrix: Vec<Vec<bool>>) -> Result<Self, AdjMatError> {
34+
// Check if the adjacency matrix is empty.
35+
if adjacency_matrix.is_empty() {
36+
return Err(AdjMatError::EmptyMat);
37+
}
38+
39+
// Validate that the adjacency matrix is square.
40+
if adjacency_matrix
41+
.iter()
42+
.any(|row| row.len() != adjacency_matrix.len())
43+
{
44+
return Err(AdjMatError::ImproperMat);
45+
}
46+
47+
Ok(Self { adjacency_matrix })
3548
}
3649

37-
/// Checks if it's safe to include vertex `v` in the Hamiltonian cycle path.
50+
/// Returns the number of vertices in the graph.
51+
fn num_vertices(&self) -> usize {
52+
self.adjacency_matrix.len()
53+
}
54+
55+
/// Determines if it is safe to include vertex `v` in the Hamiltonian cycle path.
3856
///
3957
/// # Arguments
4058
///
@@ -44,18 +62,18 @@ impl Graph {
4462
///
4563
/// # Returns
4664
///
47-
/// * `true` if it's safe to include `v` in the path, `false` otherwise.
65+
/// `true` if it is safe to include `v` in the path, `false` otherwise.
4866
fn is_safe(&self, v: usize, path: &[usize], pos: usize) -> bool {
49-
// Check if the current vertex and the last vertex in the path are adjacent
67+
// Check if the current vertex and the last vertex in the path are adjacent.
5068
if !self.adjacency_matrix[path[pos - 1]][v] {
5169
return false;
5270
}
5371

54-
// Check if the vertex has already been included in the path
72+
// Check if the vertex has already been included in the path.
5573
!path[..pos].contains(&v)
5674
}
5775

58-
/// Utility function for finding a Hamiltonian cycle recursively.
76+
/// Recursively searches for a Hamiltonian cycle.
5977
///
6078
/// This function is called by `find_hamiltonian_cycle`.
6179
///
@@ -66,15 +84,14 @@ impl Graph {
6684
///
6785
/// # Returns
6886
///
69-
/// * `true` if a Hamiltonian cycle is found, `false` otherwise.
87+
/// `true` if a Hamiltonian cycle is found, `false` otherwise.
7088
fn hamiltonian_cycle_util(&self, path: &mut Vec<usize>, pos: usize) -> bool {
71-
let vertices = self.adjacency_matrix.len();
72-
if pos == vertices {
73-
// Check if there is an edge from the last included vertex to the first vertex
89+
if pos == self.num_vertices() {
90+
// Check if there is an edge from the last included vertex to the first vertex.
7491
return self.adjacency_matrix[path[pos - 1]][path[0]];
7592
}
7693

77-
for v in 0..vertices {
94+
for v in 0..self.num_vertices() {
7895
if self.is_safe(v, path, pos) {
7996
path[pos] = v;
8097
if self.hamiltonian_cycle_util(path, pos + 1) {
@@ -87,59 +104,52 @@ impl Graph {
87104
false
88105
}
89106

90-
/// Finds a Hamiltonian cycle in the graph, if one exists, starting from the specified vertex.
107+
/// Attempts to find a Hamiltonian cycle in the graph, starting from the specified vertex.
91108
///
92-
/// A Hamiltonian cycle is a cycle that visits every vertex exactly once
93-
/// and returns to the starting vertex.
109+
/// A Hamiltonian cycle visits every vertex exactly once and returns to the starting vertex.
94110
///
95111
/// # Note
96112
/// This implementation may not find all possible Hamiltonian cycles.
97-
/// It will stop as soon as it finds one valid cycle. If multiple Hamiltonian cycles exist,
98-
/// only one of them will be returned.
113+
/// It stops as soon as it finds one valid cycle. If multiple Hamiltonian cycles exist,
114+
/// only one will be returned.
115+
///
116+
/// # Returns
99117
///
100-
/// Returns `Some(path)` if a Hamiltonian cycle is found, where `path` is a vector
118+
/// `Ok(Some(path))` if a Hamiltonian cycle is found, where `path` is a vector
101119
/// containing the indices of vertices in the cycle, starting and ending with the same vertex.
102120
///
103-
/// Returns `None` if no Hamiltonian cycle exists in the graph.
104-
fn find_hamiltonian_cycle(&self, start_vertex: usize) -> Option<Vec<usize>> {
121+
/// `Ok(None)` if no Hamiltonian cycle exists.
122+
fn find_hamiltonian_cycle(
123+
&self,
124+
start_vertex: usize,
125+
) -> Result<Option<Vec<usize>>, AdjMatError> {
126+
// Validate the start vertex.
127+
if start_vertex >= self.num_vertices() {
128+
return Err(AdjMatError::StartOutOfBound);
129+
}
130+
131+
// Initialize the path.
105132
let mut path = vec![usize::MAX; self.adjacency_matrix.len()];
106-
// Start at the specified vertex
133+
// Start at the specified vertex.
107134
path[0] = start_vertex;
108135

109136
if self.hamiltonian_cycle_util(&mut path, 1) {
110-
// To complete the cycle by returning to the starting vertex
137+
// Complete the cycle by returning to the starting vertex.
111138
path.push(start_vertex);
112-
Some(path)
139+
Ok(Some(path))
113140
} else {
114-
None
141+
Ok(None)
115142
}
116143
}
117144
}
118145

119-
/// Finds a Hamiltonian cycle in a given graph represented by an adjacency matrix, if one exists, starting from a specified vertex
146+
/// Attempts to find a Hamiltonian cycle in a graph represented by an adjacency matrix, starting from a specified vertex.
120147
pub fn find_hamiltonian_cycle(
121148
adjacency_matrix: Vec<Vec<bool>>,
122149
start_vertex: usize,
123150
) -> Result<Option<Vec<usize>>, AdjMatError> {
124-
let vertices = adjacency_matrix.len();
125-
// Check if the adjacency matrix is empty
126-
if vertices == 0 {
127-
return Err(AdjMatError::EmptyMat);
128-
}
129-
130-
// Validate maze representation (if necessary)
131-
if adjacency_matrix.iter().any(|row| row.len() != vertices) {
132-
return Err(AdjMatError::ImproperMat);
133-
}
134-
135-
// Validate start position
136-
if start_vertex >= vertices {
137-
return Err(AdjMatError::StartOutOfBound);
138-
}
139-
140-
// If validations pass, proceed with finding the cycle
141-
let graph = Graph::new(adjacency_matrix);
142-
Ok(graph.find_hamiltonian_cycle(start_vertex))
151+
let graph = Graph::new(adjacency_matrix)?;
152+
graph.find_hamiltonian_cycle(start_vertex)
143153
}
144154

145155
#[cfg(test)]

0 commit comments

Comments
 (0)