Skip to content

Commit

Permalink
Added kruskal naive algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
FilippoFantinato committed Sep 13, 2024
1 parent efd3128 commit b503ed2
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod cycles;
pub mod minimum_spanning_tree;
10 changes: 5 additions & 5 deletions src/algorithms/cycles/is_acyclic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ mod tests {
use std::ops::Deref;

use super::*;
use crate::graph::directed_graph::DirectedGraph;
use crate::graph::undirected_graph::UndirectedGraph;

#[test]
fn graph_without_cycle_success() {
let mut g = DirectedGraph::new(5);
let mut g = UndirectedGraph::new();

g.add_edge(0, 1, 1);
g.add_edge(0, 2, 1);
Expand All @@ -64,7 +64,7 @@ mod tests {

#[test]
fn graph_with_cycle_success() {
let mut g = DirectedGraph::new(5);
let mut g = UndirectedGraph::new();

g.add_edge(0, 1, 1);
g.add_edge(0, 2, 1);
Expand All @@ -80,7 +80,7 @@ mod tests {

#[test]
fn non_connected_graph_without_cycle_success() {
let mut g = DirectedGraph::new(8);
let mut g = UndirectedGraph::new();

g.add_edge(0, 1, 1);
g.add_edge(0, 2, 1);
Expand All @@ -97,7 +97,7 @@ mod tests {

#[test]
fn non_connected_graph_with_cycle_success() {
let mut g = DirectedGraph::new(8);
let mut g = UndirectedGraph::new();

g.add_edge(0, 1, 1);
g.add_edge(0, 2, 1);
Expand Down
1 change: 1 addition & 0 deletions src/algorithms/minimum_spanning_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod kruskal_naive;
114 changes: 114 additions & 0 deletions src/algorithms/minimum_spanning_tree/kruskal_naive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use crate::{
algorithms::cycles::is_acyclic::is_acyclic,
graph::{
graph::{Edge, Graph, Path},
undirected_graph::UndirectedGraph,
},
};

pub fn run(g: &dyn Graph) -> Path {
kruskal_naive(g)
}

fn kruskal_naive(g: &dyn Graph) -> Path {
let mut tmp = UndirectedGraph::new();
let mut edges: Vec<&Edge> = g.get_edges().iter().collect();
edges.sort_by_key(|e| e.2);

let mut mst = vec![];
for e in edges {
tmp.add_edge(e.0, e.1, e.2);

if is_acyclic(&tmp) {
mst.push(e.clone());
} else {
tmp.delete_edge(&e.0, &e.1);
}
}

return mst;
}

mod tests {
use crate::{
algorithms::minimum_spanning_tree::kruskal_naive::kruskal_naive,
graph::{graph::Graph, undirected_graph::UndirectedGraph},
};

#[test]
fn kruskal_naive_simple_graph_success() {
let mut g = UndirectedGraph::new();

g.add_edge(1, 2, 4993);
g.add_edge(2, 3, 1392);
g.add_edge(3, 4, 8856);
g.add_edge(4, 5, -433);
g.add_edge(5, 6, 6590);
g.add_edge(6, 7, -7462);
g.add_edge(7, 8, 6658);
g.add_edge(8, 9, -976);
g.add_edge(9, 10, 9698);

let expected = vec![
(6, 7, -7462),
(8, 9, -976),
(4, 5, -433),
(2, 3, 1392),
(1, 2, 4993),
(5, 6, 6590),
(7, 8, 6658),
(3, 4, 8856),
(9, 10, 9698),
];
let current = kruskal_naive(&g);
assert_eq!(expected, current);
}

#[test]
fn kruskal_naive_graph_with_cycle_success() {
let mut g = UndirectedGraph::new();

g.add_edge(1, 2, 4993);
g.add_edge(1, 5, 2432);
g.add_edge(2, 3, 1392);
g.add_edge(2, 4, 4687);
g.add_edge(2, 6, -34);
g.add_edge(3, 4, 8856);
g.add_edge(3, 7, 844);
g.add_edge(3, 8, -433);
g.add_edge(5, 9, -432);
g.add_edge(6, 9, -7462);
g.add_edge(6, 7, 442);
g.add_edge(8, 10, -976);

let expected = vec![
(6, 9, -7462),
(8, 10, -976),
(3, 8, -433),
(5, 9, -432),
(2, 6, -34),
(6, 7, 442),
(3, 7, 844),
(1, 5, 2432),
(2, 4, 4687),
];
let current = kruskal_naive(&g);
assert_eq!(expected, current);
}

#[test]
fn kruskal_naive_full_connected_graph_success() {
let mut g = UndirectedGraph::new();

g.add_edge(1, 2, -544);
g.add_edge(1, 3, 455);
g.add_edge(1, 4, -12);
g.add_edge(2, 3, 84);
g.add_edge(2, 4, 27);
g.add_edge(3, 4, -7);

let expected = vec![(1, 2, -544), (1, 4, -12), (3, 4, -7)];
let current = kruskal_naive(&g);
assert_eq!(expected, current);
}
}
30 changes: 19 additions & 11 deletions src/cli/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::rc::Rc;

use crate::algorithms::cycles;
use crate::graph::directed_graph::DirectedGraph;
use crate::graph::graph::Graph;
use crate::algorithms::{cycles, minimum_spanning_tree};
use crate::graph::graph::{Graph, Weight};
use crate::graph::undirected_graph::UndirectedGraph;

#[derive(clap::ValueEnum, Clone, Debug)]
enum Algorithm {
IsAcyclic,
KruskalNaive,
}

/// Simple program to greet a person
Expand All @@ -30,43 +31,50 @@ pub fn run_cli() {
match args.algorithm {
Algorithm::IsAcyclic => {
let g = read_graph(args.file);
let res = cycles::is_acyclic::run(g.deref());
let res = cycles::is_acyclic::run(&g);

println!("Is acylic result: {:}", res);
}
Algorithm::KruskalNaive => {
let g = read_graph(args.file);
let path = minimum_spanning_tree::kruskal_naive::run(&g);
let weight: i128 = path.iter().map(|e| (*e).2).sum();

println!("Kruskal naive path: {:?}", path);
println!("Kruskal naive weight: {:?}", weight);
}
}
}

fn read_graph(path: PathBuf) -> Box<dyn Graph> {
fn read_graph(path: PathBuf) -> impl Graph {
let lines = fs::read_to_string(path).unwrap();
let mut lines = lines.lines();
let mut header = lines
.next()
.unwrap_or_else(|| panic!("Invalid format"))
.split_whitespace();
let n = header
let _n = header
.next()
.map(|v| v.parse::<usize>().unwrap())
.unwrap_or_else(|| panic!("Invalid format"));
let mut g = Box::new(DirectedGraph::new(n));
let mut g = UndirectedGraph::new();

lines.for_each(|v| {
let mut line = v.split_whitespace();
let u = line
.next()
.map(|v| v.parse::<usize>().unwrap() - 1)
.map(|v| v.parse::<usize>().unwrap())
.unwrap_or_else(|| panic!("Invalid format"));
let v = line
.next()
.map(|v| v.parse::<usize>().unwrap() - 1)
.map(|v| v.parse::<usize>().unwrap())
.unwrap_or_else(|| panic!("Invalid format"));
let w = line
.next()
.map(|v| v.parse::<i128>().unwrap() - 1)
.map(|v| v.parse::<i128>().unwrap())
.unwrap_or_else(|| panic!("Invalid format"));

g.add_edge(u, v, w);
g.add_edge(v, u, w);
});

return g;
Expand Down
2 changes: 1 addition & 1 deletion src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod directed_graph;
pub mod graph;
pub mod undirected_graph;
4 changes: 4 additions & 0 deletions src/graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ use std::collections::{HashMap, HashSet};

pub type Vertex = usize;
pub type Weight = i128;
pub type Edge = (Vertex, Vertex, Weight);
pub type Path = Vec<Edge>;

pub trait Graph {
fn add_edge(&mut self, u: Vertex, v: Vertex, w: Weight);
fn _get_size(&self) -> usize;
fn _get_adj_list(&self, v: &Vertex) -> Option<&HashMap<Vertex, Weight>>;
fn get_weight(&self, u: &Vertex, v: &Vertex) -> Option<&Weight>;
fn get_vertices(&self) -> &HashSet<Vertex>;
fn get_edges(&self) -> &HashSet<Edge>;
fn delete_edge(&mut self, u: &Vertex, v: &Vertex);
}
36 changes: 28 additions & 8 deletions src/graph/directed_graph.rs → src/graph/undirected_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@ use std::collections::{HashMap, HashSet};

use crate::graph::graph::Graph;

use super::graph::{Vertex, Weight};
use super::graph::{Edge, Vertex, Weight};

pub struct DirectedGraph {
pub struct UndirectedGraph {
adj_matrix: HashMap<Vertex, HashMap<Vertex, Weight>>,
vertices: HashSet<Vertex>,
_size: usize,
edges: HashSet<Edge>,
}

impl DirectedGraph {
pub fn new(size: usize) -> DirectedGraph {
DirectedGraph {
impl UndirectedGraph {
pub fn new() -> UndirectedGraph {
UndirectedGraph {
adj_matrix: HashMap::new(),
vertices: HashSet::new(),
_size: size,
edges: HashSet::new(),
}
}
}

impl Graph for DirectedGraph {
impl Graph for UndirectedGraph {
fn add_edge(&mut self, u: Vertex, v: Vertex, w: Weight) {
if self.adj_matrix.get(&u).is_none() {
self.adj_matrix.insert(u, HashMap::new());
}
if self.adj_matrix.get(&v).is_none() {
self.adj_matrix.insert(v, HashMap::new());
}
self.adj_matrix.get_mut(&u).unwrap().insert(v, w);
self.adj_matrix.get_mut(&v).unwrap().insert(u, w);

self.vertices.insert(u);
self.vertices.insert(v);

self.edges
.insert(if u <= v { (u, v, w) } else { (v, u, w) });
}

fn _get_size(&self) -> usize {
Expand All @@ -46,4 +53,17 @@ impl Graph for DirectedGraph {
fn get_vertices(&self) -> &HashSet<Vertex> {
&self.vertices
}

fn get_edges(&self) -> &HashSet<Edge> {
&self.edges
}

fn delete_edge(&mut self, u: &Vertex, v: &Vertex) {
self.get_weight(u, v).cloned().map(|w| {
let e = if u <= v { (*u, *v, w) } else { (*v, *u, w) };
self.adj_matrix.get_mut(u).unwrap().remove(v);
self.adj_matrix.get_mut(v).unwrap().remove(u);
self.edges.remove(&e);
});
}
}

0 comments on commit b503ed2

Please sign in to comment.