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,
|
/// 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]`.
|
/// 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 {
|
pub fn is_hamilton_cycle(&self, path: &[V]) -> bool {
|
||||||
!path.is_empty()
|
(path.is_empty() && self.vertex_count() == 0)
|
||||||
&& self.is_hamilton_path(path)
|
|| (!path.is_empty()
|
||||||
&& self.has_edge((path.last().unwrap(), path.first().unwrap()))
|
&& 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> {
|
pub fn iter(&self) -> impl Iterator<Item = &V> {
|
||||||
@ -404,6 +438,39 @@ where
|
|||||||
&& self.has_edge((path.last().unwrap(), path.first().unwrap()))
|
&& 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> {
|
pub fn iter(&self) -> impl Iterator<Item = &V> {
|
||||||
self.vertices.iter()
|
self.vertices.iter()
|
||||||
}
|
}
|
||||||
@ -427,6 +494,6 @@ macro_rules! graph {
|
|||||||
$( vertices.insert($e); )*
|
$( 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() {
|
fn main() {
|
||||||
// test graph:
|
// test graph:
|
||||||
// ┏━━━━━1━━━━━┓
|
// ┏━━━1━━━┓
|
||||||
// -> 0 ┃ 3
|
// -> 0 ┃ 3
|
||||||
// ┗━━━━━2━━━━━┛
|
// ┗━━━2━━━┛
|
||||||
|
|
||||||
// adjacency list
|
// adjacency list
|
||||||
let g = graph! {
|
let g = graph! {
|
||||||
@ -129,9 +129,9 @@ fn main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// test graph:
|
// test graph:
|
||||||
// ┏━━━━━1━━━━━┓
|
// ┏━━━1━━━┓
|
||||||
// -> 0 ┃ 3
|
// -> 0 ┃ 3
|
||||||
// ┗━━━━━2━━━━━┛
|
// ┗━━━2━━━┛
|
||||||
|
|
||||||
let g = graph! {
|
let g = graph! {
|
||||||
0: 1, 2;
|
0: 1, 2;
|
||||||
@ -164,6 +164,66 @@ fn main() {
|
|||||||
"Cycle that does not contain all vertices is not a hamilton cycle"
|
"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
|
// yay
|
||||||
println!("All tests passed.");
|
println!("All tests passed.");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user