1
- //! This module provides functionality to find a Hamiltonian cycle in graph.
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.
5
+ #[ derive( Debug , PartialEq , Eq ) ]
6
+ pub enum AdjMatError {
7
+ /// The adjacency matrix is empty.
8
+ EmptyMat ,
9
+ /// The adjacency matrix is not square.
10
+ ImproperMat ,
11
+ /// The start vertex is out of bounds.
12
+ StartOutOfBound ,
13
+ }
14
+
4
15
/// Represents a graph with an adjacency matrix.
5
16
struct Graph {
6
17
/// The adjacency matrix representing the graph.
7
- adjacency_matrix : Vec < Vec < u8 > > ,
8
- /// The number of vertices in the graph.
9
- vertices : usize ,
18
+ adjacency_matrix : Vec < Vec < bool > > ,
10
19
}
11
20
12
21
impl Graph {
@@ -15,14 +24,14 @@ impl Graph {
15
24
/// # Arguments
16
25
///
17
26
/// * `adjacency_matrix` - A square matrix where each element represents
18
- /// the presence `1 ` or absence `0 ` of an edge
19
- /// between two vertices.
20
- fn new ( adjacency_matrix : Vec < Vec < u8 > > ) -> Self {
21
- let vertices = adjacency_matrix . len ( ) ;
22
- Self {
23
- adjacency_matrix ,
24
- vertices ,
25
- }
27
+ /// the presence `true ` or absence `false ` of an edge
28
+ /// between two vertices.
29
+ ///
30
+ /// # Returns
31
+ ///
32
+ /// A graph that represents adjacency matrix
33
+ fn new ( adjacency_matrix : Vec < Vec < bool > > ) -> Self {
34
+ Self { adjacency_matrix }
26
35
}
27
36
28
37
/// Checks if it's safe to include vertex `v` in the Hamiltonian cycle path.
@@ -38,7 +47,7 @@ impl Graph {
38
47
/// * `true` if it's safe to include `v` in the path, `false` otherwise.
39
48
fn is_safe ( & self , v : usize , path : & [ usize ] , pos : usize ) -> bool {
40
49
// Check if the current vertex and the last vertex in the path are adjacent
41
- if self . adjacency_matrix [ path[ pos - 1 ] ] [ v] == 0 {
50
+ if ! self . adjacency_matrix [ path[ pos - 1 ] ] [ v] {
42
51
return false ;
43
52
}
44
53
@@ -48,7 +57,7 @@ impl Graph {
48
57
49
58
/// Utility function for finding a Hamiltonian cycle recursively.
50
59
///
51
- /// This function is called recursively by `find_hamiltonian_cycle`.
60
+ /// This function is called by `find_hamiltonian_cycle`.
52
61
///
53
62
/// # Arguments
54
63
///
@@ -59,18 +68,19 @@ impl Graph {
59
68
///
60
69
/// * `true` if a Hamiltonian cycle is found, `false` otherwise.
61
70
fn hamiltonian_cycle_util ( & self , path : & mut Vec < usize > , pos : usize ) -> bool {
62
- if pos == self . vertices {
71
+ let vertices = self . adjacency_matrix . len ( ) ;
72
+ if pos == vertices {
63
73
// Check if there is an edge from the last included vertex to the first vertex
64
- return self . adjacency_matrix [ path[ pos - 1 ] ] [ path[ 0 ] ] == 1 ;
74
+ return self . adjacency_matrix [ path[ pos - 1 ] ] [ path[ 0 ] ] ;
65
75
}
66
76
67
- for v in 0 ..self . vertices {
77
+ for v in 0 ..vertices {
68
78
if self . is_safe ( v, path, pos) {
69
79
path[ pos] = v;
70
80
if self . hamiltonian_cycle_util ( path, pos + 1 ) {
71
81
return true ;
72
82
}
73
- path[ pos] = std :: usize:: MAX ;
83
+ path[ pos] = usize:: MAX ;
74
84
}
75
85
}
76
86
@@ -82,16 +92,23 @@ impl Graph {
82
92
/// A Hamiltonian cycle is a cycle that visits every vertex exactly once
83
93
/// and returns to the starting vertex.
84
94
///
95
+ /// # Note
96
+ /// 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.
99
+ ///
85
100
/// Returns `Some(path)` if a Hamiltonian cycle is found, where `path` is a vector
86
101
/// containing the indices of vertices in the cycle, starting and ending with the same vertex.
87
102
///
88
103
/// Returns `None` if no Hamiltonian cycle exists in the graph.
89
104
fn find_hamiltonian_cycle ( & self , start_vertex : usize ) -> Option < Vec < usize > > {
90
- let mut path = vec ! [ std:: usize :: MAX ; self . vertices] ;
91
- path[ 0 ] = start_vertex; // Start at the specified vertex
105
+ let mut path = vec ! [ usize :: MAX ; self . adjacency_matrix. len( ) ] ;
106
+ // Start at the specified vertex
107
+ path[ 0 ] = start_vertex;
92
108
93
109
if self . hamiltonian_cycle_util ( & mut path, 1 ) {
94
- path. push ( start_vertex) ; // To complete the cycle by returning to the starting vertex
110
+ // To complete the cycle by returning to the starting vertex
111
+ path. push ( start_vertex) ;
95
112
Some ( path)
96
113
} else {
97
114
None
@@ -101,11 +118,28 @@ impl Graph {
101
118
102
119
/// Finds a Hamiltonian cycle in a given graph represented by an adjacency matrix, if one exists, starting from a specified vertex
103
120
pub fn find_hamiltonian_cycle (
104
- adjacency_matrix : Vec < Vec < u8 > > ,
121
+ adjacency_matrix : Vec < Vec < bool > > ,
105
122
start_vertex : usize ,
106
- ) -> Option < Vec < usize > > {
123
+ ) -> 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
107
141
let graph = Graph :: new ( adjacency_matrix) ;
108
- graph. find_hamiltonian_cycle ( start_vertex)
142
+ Ok ( graph. find_hamiltonian_cycle ( start_vertex) )
109
143
}
110
144
111
145
#[ cfg( test) ]
@@ -128,53 +162,77 @@ mod tests {
128
162
hamiltonian_cycle_tests ! {
129
163
test_complete_graph: (
130
164
vec![
131
- vec![ 0 , 1 , 1 , 1 ] ,
132
- vec![ 1 , 0 , 1 , 1 ] ,
133
- vec![ 1 , 1 , 0 , 1 ] ,
134
- vec![ 1 , 1 , 1 , 0 ] ,
165
+ vec![ false , true , true , true ] ,
166
+ vec![ true , false , true , true ] ,
167
+ vec![ true , true , false , true ] ,
168
+ vec![ true , true , true , false ] ,
135
169
] ,
136
170
0 ,
137
- Some ( vec![ 0 , 1 , 2 , 3 , 0 ] )
171
+ Ok ( Some ( vec![ 0 , 1 , 2 , 3 , 0 ] ) )
138
172
) ,
139
173
test_cycle_graph: (
140
174
vec![
141
- vec![ 0 , 1 , 0 , 0 , 1 ] ,
142
- vec![ 1 , 0 , 1 , 0 , 0 ] ,
143
- vec![ 0 , 1 , 0 , 1 , 0 ] ,
144
- vec![ 0 , 0 , 1 , 0 , 1 ] ,
145
- vec![ 1 , 0 , 0 , 1 , 0 ] ,
175
+ vec![ false , true , false , false , true ] ,
176
+ vec![ true , false , true , false , false ] ,
177
+ vec![ false , true , false , true , false ] ,
178
+ vec![ false , false , true , false , true ] ,
179
+ vec![ true , false , false , true , false ] ,
146
180
] ,
147
181
2 ,
148
- Some ( vec![ 2 , 1 , 0 , 4 , 3 , 2 ] )
182
+ Ok ( Some ( vec![ 2 , 1 , 0 , 4 , 3 , 2 ] ) )
149
183
) ,
150
184
test_no_cycle_graph: (
151
185
vec![
152
- vec![ 0 , 1 , 0 ] ,
153
- vec![ 1 , 0 , 0 ] ,
154
- vec![ 0 , 0 , 0 ] ,
186
+ vec![ false , true , false ] ,
187
+ vec![ true , false , false ] ,
188
+ vec![ false , false , false ] ,
155
189
] ,
156
190
0 ,
157
- None :: <Vec <usize >>
191
+ Ok ( None :: <Vec <usize >>)
158
192
) ,
159
193
test_triangle_graph: (
160
194
vec![
161
- vec![ 0 , 1 , 1 ] ,
162
- vec![ 1 , 0 , 1 ] ,
163
- vec![ 1 , 1 , 0 ] ,
195
+ vec![ false , true , true ] ,
196
+ vec![ true , false , true ] ,
197
+ vec![ true , true , false ] ,
164
198
] ,
165
199
1 ,
166
- Some ( vec![ 1 , 0 , 2 , 1 ] )
200
+ Ok ( Some ( vec![ 1 , 0 , 2 , 1 ] ) )
167
201
) ,
168
202
test_tree_graph: (
169
203
vec![
170
- vec![ 0 , 1 , 0 , 1 , 0 ] ,
171
- vec![ 1 , 0 , 1 , 1 , 0 ] ,
172
- vec![ 0 , 1 , 0 , 0 , 0 ] ,
173
- vec![ 1 , 0 , 0 , 0 , 1 ] ,
174
- vec![ 0 , 0 , 0 , 1 , 0 ] ,
204
+ vec![ false , true , false , true , false ] ,
205
+ vec![ true , false , true , true , false ] ,
206
+ vec![ false , true , false , false , false ] ,
207
+ vec![ true , true , false , false , true ] ,
208
+ vec![ false , false , false , true , false ] ,
175
209
] ,
176
210
0 ,
177
- None :: <Vec <usize >>
211
+ Ok ( None :: <Vec <usize >>)
212
+ ) ,
213
+ test_empty_matrix: (
214
+ vec![ ] ,
215
+ 0 ,
216
+ Err ( AdjMatError :: EmptyMat )
217
+ ) ,
218
+ test_improper_matrix: (
219
+ vec![
220
+ vec![ false , true ] ,
221
+ vec![ true ] ,
222
+ vec![ false , true , true ] ,
223
+ vec![ true , true , true , false ]
224
+ ] ,
225
+ 0 ,
226
+ Err ( AdjMatError :: ImproperMat )
227
+ ) ,
228
+ test_start_out_of_bound: (
229
+ vec![
230
+ vec![ false , true , true ] ,
231
+ vec![ true , false , true ] ,
232
+ vec![ true , true , false ] ,
233
+ ] ,
234
+ 3 ,
235
+ Err ( AdjMatError :: StartOutOfBound )
178
236
) ,
179
237
}
180
238
}
0 commit comments