add euler path/cycle validation
This commit is contained in:
parent
74b438efaa
commit
f50cec4923
77
src/graph.rs
77
src/graph.rs
@ -139,11 +139,45 @@ where
|
||||
/// 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.
|
||||
/// empty paths are considered a hamilton cycle for graphs with no vertices.
|
||||
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()))
|
||||
(path.is_empty() && self.vertex_count() == 0)
|
||||
|| (!path.is_empty()
|
||||
&& self.is_hamilton_path(path)
|
||||
&& self.has_edge((path.last().unwrap(), path.first().unwrap())))
|
||||
}
|
||||
|
||||
/// check if a provided path is an euler path within the graph,
|
||||
/// meaning it contains all of the graphs edges exactly once.
|
||||
///
|
||||
/// an empty path is considered an euler path for graphs with no edges.
|
||||
pub fn is_euler_path(&self, path: &[(V, V)]) -> bool {
|
||||
match self.edge_count() {
|
||||
0 => path.is_empty(),
|
||||
n => {
|
||||
let edges_connected = path.windows(2).all(|w| w[0].1 == w[1].0);
|
||||
|
||||
let path_edges = path.iter().collect::<HashSet<_>>();
|
||||
let has_all_edges_once = path.len() == n
|
||||
&& self.edges.iter().all(|(v, ws)| {
|
||||
ws.iter()
|
||||
.all(|w| path_edges.contains(&(v.clone(), w.clone())))
|
||||
});
|
||||
|
||||
edges_connected && has_all_edges_once
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// check if a provided path is an euler cycle within the graph,
|
||||
/// meaning it is a cycle and contains all of the graphs edges exactly once.
|
||||
///
|
||||
/// an empty path are considered an euler cycle for graphs with no edges.
|
||||
pub fn is_euler_cycle(&self, path: &[(V, V)]) -> bool {
|
||||
(path.is_empty() && self.edge_count() == 0)
|
||||
|| (!path.is_empty()
|
||||
&& self.is_euler_path(path)
|
||||
&& path.first().unwrap().0 == path.last().unwrap().1)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &V> {
|
||||
@ -404,6 +438,39 @@ where
|
||||
&& self.has_edge((path.last().unwrap(), path.first().unwrap()))
|
||||
}
|
||||
|
||||
/// check if a provided path is an euler path within the graph,
|
||||
/// meaning it contains all of the graphs edges exactly once.
|
||||
///
|
||||
/// an empty path is considered an euler path for graphs with no edges.
|
||||
pub fn is_euler_path(&self, path: &[(V, V)]) -> bool {
|
||||
match self.edge_count() {
|
||||
0 => path.is_empty(),
|
||||
n => {
|
||||
let edges_connected = path.windows(2).all(|w| w[0].1 == w[1].0);
|
||||
|
||||
let path_edges = path.iter().collect::<HashSet<_>>();
|
||||
let has_all_edges_once = path.len() == n
|
||||
&& self.edges.iter().all(|(v, ws)| {
|
||||
ws.iter()
|
||||
.all(|(_, w)| path_edges.contains(&(v.clone(), w.clone())))
|
||||
});
|
||||
|
||||
edges_connected && has_all_edges_once
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// check if a provided path is an euler cycle within the graph,
|
||||
/// meaning it is a cycle and contains all of the graphs edges exactly once.
|
||||
///
|
||||
/// an empty path are considered an euler cycle for graphs with no edges.
|
||||
pub fn is_euler_cycle(&self, path: &[(V, V)]) -> bool {
|
||||
(path.is_empty() && self.edge_count() == 0)
|
||||
|| (!path.is_empty()
|
||||
&& self.is_euler_path(path)
|
||||
&& path.first().unwrap().0 == path.last().unwrap().1)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &V> {
|
||||
self.vertices.iter()
|
||||
}
|
||||
@ -427,6 +494,6 @@ macro_rules! graph {
|
||||
$( vertices.insert($e); )*
|
||||
)*
|
||||
|
||||
Graph { vertices, edges }
|
||||
Graph { vertices, edges }
|
||||
}};
|
||||
}
|
||||
|
||||
72
src/main.rs
72
src/main.rs
@ -5,9 +5,9 @@ pub use graph::{Graph, WeightedGraph};
|
||||
|
||||
fn main() {
|
||||
// test graph:
|
||||
// ┏━━━━━1━━━━━┓
|
||||
// -> 0 ┃ 3
|
||||
// ┗━━━━━2━━━━━┛
|
||||
// ┏━━━1━━━┓
|
||||
// -> 0 ┃ 3
|
||||
// ┗━━━2━━━┛
|
||||
|
||||
// adjacency list
|
||||
let g = graph! {
|
||||
@ -129,9 +129,9 @@ fn main() {
|
||||
);
|
||||
|
||||
// test graph:
|
||||
// ┏━━━━━1━━━━━┓
|
||||
// -> 0 ┃ 3
|
||||
// ┗━━━━━2━━━━━┛
|
||||
// ┏━━━1━━━┓
|
||||
// -> 0 ┃ 3
|
||||
// ┗━━━2━━━┛
|
||||
|
||||
let g = graph! {
|
||||
0: 1, 2;
|
||||
@ -164,6 +164,66 @@ fn main() {
|
||||
"Cycle that does not contain all vertices is not a hamilton cycle"
|
||||
);
|
||||
|
||||
// test graph:
|
||||
// ┏━━━1━━━┓
|
||||
// -> 0 3
|
||||
// ┗━━━2━━━┛
|
||||
|
||||
let g = graph! {
|
||||
0: 1;
|
||||
1: 3;
|
||||
2: 0;
|
||||
3: 2;
|
||||
};
|
||||
|
||||
let path = [(0, 1), (1, 3), (3, 2), (2, 0)];
|
||||
assert!(
|
||||
g.is_euler_path(&path),
|
||||
"Valid euler path in graph should be recognized as such"
|
||||
);
|
||||
|
||||
let path = [(0, 1), (1, 2), (2, 3), (3, 0)];
|
||||
assert!(
|
||||
!g.is_euler_path(&path),
|
||||
"A path with a non-existent edge is not a euler path"
|
||||
);
|
||||
|
||||
// test graph:
|
||||
// ┏━━━1━━━┓
|
||||
// -> 0 ┃ 3
|
||||
// ┗━━━2━━━┛
|
||||
|
||||
let g = graph! {
|
||||
0: 1;
|
||||
1: 2, 3;
|
||||
2: 0;
|
||||
3: 2;
|
||||
};
|
||||
|
||||
let path = [(0, 1), (1, 3), (3, 2), (2, 0)];
|
||||
assert!(
|
||||
!g.is_euler_path(&path),
|
||||
"A path with missing edges is not an euler path"
|
||||
);
|
||||
|
||||
// test graph:
|
||||
// ┏━>━1━>━┓
|
||||
// -> 0 3
|
||||
// ┗━<━2━━━┛
|
||||
|
||||
let g = graph! {
|
||||
0: 1;
|
||||
1: 3;
|
||||
2: 0, 3;
|
||||
3: 2;
|
||||
};
|
||||
|
||||
let path = [(0, 1), (1, 3), (3, 2), (2, 3), (3, 2), (2, 0)];
|
||||
assert!(
|
||||
!g.is_euler_path(&path),
|
||||
"A path with duplicate edges is not an euler path"
|
||||
);
|
||||
|
||||
// yay
|
||||
println!("All tests passed.");
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user