add hamilton path/cycle validation

This commit is contained in:
mxhagen 2025-04-08 23:26:53 +02:00
parent 2ccf946ccf
commit dfbc6c309a
2 changed files with 120 additions and 0 deletions

View File

@ -116,6 +116,36 @@ where
None
}
/// check if a provided path is a hamilton path within the graph,
/// meaning it contains all of the graphs vertices exactly once.
///
/// an empty path is considered hamiltonian for empty graphs.
pub fn is_hamilton_path(&self, path: &[V]) -> bool {
match self.vertex_count() {
0 => path.is_empty(),
n => {
let edges_exist = path.windows(2).all(|w| self.has_edge((&w[0], &w[1])));
let has_all_verts_once =
path.len() == n && self.vertices == path.iter().cloned().collect();
edges_exist && has_all_verts_once
}
}
}
/// check if a provided path is a hamilton cycle within the graph,
/// meaning it is a cycle and contains all of the graphs vertices exactly once.
///
/// this assumes a path only containing the start/end vertex once,
/// so a valid 2-vertex-loop would be represented as `[v, w]` and not `[v, w, v]`.
///
/// empty paths are never considered a cycle.
pub fn is_hamilton_cycle(&self, path: &[V]) -> bool {
!path.is_empty()
&& self.is_hamilton_path(path)
&& self.has_edge((path.last().unwrap(), path.first().unwrap()))
}
pub fn iter(&self) -> impl Iterator<Item = &V> {
self.vertices.iter()
}
@ -344,6 +374,36 @@ where
None
}
/// check if a provided path is a hamilton path within the graph,
/// meaning it contains all of the graphs vertices exactly once.
///
/// an empty path is considered hamiltonian for empty graphs.
pub fn is_hamilton_path(&self, path: &[V]) -> bool {
match self.vertex_count() {
0 => path.is_empty(),
n => {
let edges_exist = path.windows(2).all(|w| self.has_edge((&w[0], &w[1])));
let has_all_verts_once =
path.len() == n && self.vertices == path.iter().cloned().collect();
edges_exist && has_all_verts_once
}
}
}
/// check if a provided path is a hamilton cycle within the graph,
/// meaning it is a cycle and contains all of the graphs vertices exactly once.
///
/// this assumes a path only containing the start/end vertex once,
/// so a valid 2-vertex-loop would be represented as `[v, w]` and not `[v, w, v]`.
///
/// empty paths are never considered a cycle.
pub fn is_hamilton_cycle(&self, path: &[V]) -> bool {
!path.is_empty()
&& self.is_hamilton_path(path)
&& self.has_edge((path.last().unwrap(), path.first().unwrap()))
}
pub fn iter(&self) -> impl Iterator<Item = &V> {
self.vertices.iter()
}

View File

@ -104,6 +104,66 @@ fn main() {
"Dijkstra should find cheapest way in weighted example graph"
);
let path = ['a', 'b', 'c', 'd', 'e', 'f'];
assert!(
g.is_hamilton_path(&path),
"Valid hamilton path in graph should be recognized as such"
);
let path = ['a', 'd', 'c', 'b', 'e', 'f'];
assert!(
!g.is_hamilton_path(&path),
"A path with a non-existent edge is not hamiltonian"
);
let path = ['a', 'b', 'c', 'd', 'e'];
assert!(
!g.is_hamilton_path(&path),
"A path with missing vertices is not hamiltonian"
);
let path = ['f', 'e', 'd', 'c', 'b', 'a', 'b'];
assert!(
!g.is_hamilton_path(&path),
"A path with duplicate vertices is not hamiltonian"
);
// test graph:
// ┏━━━━━1━━━━━┓
// -> 0 ┃ 3
// ┗━━━━━2━━━━━┛
let g = graph! {
0: 1, 2;
1: 0, 2, 3;
2: 0, 1, 3;
3: 1, 2;
};
let path = [0, 1, 3, 2];
assert!(
g.is_hamilton_cycle(&path),
"Valid hamilton cycle in graph should be recognized as such"
);
let path = [0, 3, 1, 2];
assert!(
!g.is_hamilton_cycle(&path),
"A path with a non-existent edge is not a hamilton cycle"
);
let path = [0, 1, 2, 3];
assert!(
!g.is_hamilton_cycle(&path),
"Hamilton path that does not loop is not a hamilton cycle"
);
let path = [0, 1, 2];
assert!(
!g.is_hamilton_cycle(&path),
"Cycle that does not contain all vertices is not a hamilton cycle"
);
// yay
println!("All tests passed.");
}