1
1
//! This module provides functionality to find a Hamiltonian cycle in a graph.
2
2
//! Source: [Uncyclopedia](https://en.wikipedia.org/wiki/Hamiltonian_path_problem)
3
3
4
- /// Represents errors that can occur while working with an adjacency matrix.
4
+ /// Represents potential errors when working with an adjacency matrix.
5
5
#[ derive( Debug , PartialEq , Eq ) ]
6
6
pub enum AdjMatError {
7
- /// The adjacency matrix is empty.
7
+ /// Indicates that the adjacency matrix is empty.
8
8
EmptyMat ,
9
- /// The adjacency matrix is not square.
9
+ /// Indicates that the adjacency matrix is not square.
10
10
ImproperMat ,
11
- /// The start vertex is out of bounds.
11
+ /// Indicates that the starting vertex is out of bounds.
12
12
StartOutOfBound ,
13
13
}
14
14
15
- /// Represents a graph with an adjacency matrix.
15
+ /// Represents a graph using an adjacency matrix.
16
16
struct Graph {
17
17
/// The adjacency matrix representing the graph.
18
18
adjacency_matrix : Vec < Vec < bool > > ,
19
19
}
20
20
21
21
impl Graph {
22
- /// Creates a new graph with the given adjacency matrix.
22
+ /// Creates a new graph with the provided adjacency matrix.
23
23
///
24
24
/// # Arguments
25
25
///
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
28
28
/// between two vertices.
29
29
///
30
30
/// # Returns
31
31
///
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 } )
35
48
}
36
49
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.
38
56
///
39
57
/// # Arguments
40
58
///
@@ -44,18 +62,18 @@ impl Graph {
44
62
///
45
63
/// # Returns
46
64
///
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.
48
66
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.
50
68
if !self . adjacency_matrix [ path[ pos - 1 ] ] [ v] {
51
69
return false ;
52
70
}
53
71
54
- // Check if the vertex has already been included in the path
72
+ // Check if the vertex has already been included in the path.
55
73
!path[ ..pos] . contains ( & v)
56
74
}
57
75
58
- /// Utility function for finding a Hamiltonian cycle recursively .
76
+ /// Recursively searches for a Hamiltonian cycle.
59
77
///
60
78
/// This function is called by `find_hamiltonian_cycle`.
61
79
///
@@ -66,15 +84,14 @@ impl Graph {
66
84
///
67
85
/// # Returns
68
86
///
69
- /// * `true` if a Hamiltonian cycle is found, `false` otherwise.
87
+ /// `true` if a Hamiltonian cycle is found, `false` otherwise.
70
88
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.
74
91
return self . adjacency_matrix [ path[ pos - 1 ] ] [ path[ 0 ] ] ;
75
92
}
76
93
77
- for v in 0 ..vertices {
94
+ for v in 0 ..self . num_vertices ( ) {
78
95
if self . is_safe ( v, path, pos) {
79
96
path[ pos] = v;
80
97
if self . hamiltonian_cycle_util ( path, pos + 1 ) {
@@ -87,59 +104,52 @@ impl Graph {
87
104
false
88
105
}
89
106
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.
91
108
///
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.
94
110
///
95
111
/// # Note
96
112
/// 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
99
117
///
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
101
119
/// containing the indices of vertices in the cycle, starting and ending with the same vertex.
102
120
///
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.
105
132
let mut path = vec ! [ usize :: MAX ; self . adjacency_matrix. len( ) ] ;
106
- // Start at the specified vertex
133
+ // Start at the specified vertex.
107
134
path[ 0 ] = start_vertex;
108
135
109
136
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.
111
138
path. push ( start_vertex) ;
112
- Some ( path)
139
+ Ok ( Some ( path) )
113
140
} else {
114
- None
141
+ Ok ( None )
115
142
}
116
143
}
117
144
}
118
145
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.
120
147
pub fn find_hamiltonian_cycle (
121
148
adjacency_matrix : Vec < Vec < bool > > ,
122
149
start_vertex : usize ,
123
150
) -> 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)
143
153
}
144
154
145
155
#[ cfg( test) ]
0 commit comments