Skip to content

Commit

Permalink
Merge pull request #40 from azizkayumov/index
Browse files Browse the repository at this point in the history
Node deletion - reuses the space allocated for deleted nodes to create new nodes.
  • Loading branch information
azizkayumov committed Nov 4, 2023
2 parents c061cfc + 12dd9e6 commit 17055c8
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 171 deletions.
57 changes: 57 additions & 0 deletions src/index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
pub struct Index {
pub time_id: usize,
deleted_ids: Vec<usize>, // maybe use a set instead?
}

impl Index {
pub fn new() -> Self {
Self {
time_id: 0,
deleted_ids: Vec::new(),
}
}

pub fn insert(&mut self) -> usize {
if !self.deleted_ids.is_empty() {
return self.deleted_ids.pop().unwrap();
}
self.time_id += 1;
self.time_id - 1
}

pub fn delete(&mut self, id: usize) {
assert!(id < self.time_id, "Invalid deletion");
self.deleted_ids.push(id);
}
}

#[cfg(test)]
mod tests {

#[test]
pub fn test_indexing() {
let mut index = super::Index::new();
// make 3 insertions
assert_eq!(index.insert(), 0);
assert_eq!(index.insert(), 1);
assert_eq!(index.insert(), 2);

// delete 1
index.delete(1);

// next insertion should be 1
assert_eq!(index.insert(), 1);
}

#[test]
#[should_panic]
pub fn test_invalid_deletion() {
let mut index = super::Index::new();
// make 3 insertions
assert_eq!(index.insert(), 0);
assert_eq!(index.insert(), 1);
assert_eq!(index.insert(), 2);

index.delete(4);
}
}
54 changes: 51 additions & 3 deletions src/lctree.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
use crate::{
index::Index,
node::{Node, Parent},
path::{FindMax, Path},
splay::{normalize, splay, update},
};

pub struct LinkCutTree<T: Path> {
forest: Vec<Node<T>>,
index: Index,
}

impl<T: Path> LinkCutTree<T> {
#[must_use]
pub fn new() -> Self {
Self { forest: Vec::new() }
Self {
forest: Vec::new(),
index: Index::new(),
}
}

pub fn make_tree(&mut self, weight: f64) -> usize {
let idx = self.forest.len();
let idx = self.index.insert();
if idx < self.forest.len() {
self.forest[idx] = Node::new(idx, weight);
return idx;
}
self.forest.push(Node::new(idx, weight));
idx
}

/// Delete a tree from the forest
/// # Panics
///
/// Panics if the tree contains more than one node.
pub fn remove_tree(&mut self, idx: usize) {
assert!(
self.forest[idx].degree == 0,
"Invalid deletion: tree contains more than one node."
);
self.index.delete(idx);
}

/// Constructs a path from a node to the root of the tree.
fn access(&mut self, v: usize) {
splay(&mut self.forest, v);
Expand Down Expand Up @@ -68,6 +89,8 @@ impl<T: Path> LinkCutTree<T> {
// v is the root of its represented tree, so no need to check if it has a left child
self.forest[v].left = Some(w);
self.forest[w].parent = Parent::Node(v);
self.forest[v].degree += 1;
self.forest[w].degree += 1;
}

/// Cuts the link between nodes v and w (if it exists)
Expand All @@ -79,11 +102,13 @@ impl<T: Path> LinkCutTree<T> {
if let Some(left) = self.forest[w].left {
if left != v || self.forest[v].right.is_some() {
// maybe this should be a panic?
eprintln!("Error: no link between {v} and {w}");
// eprintln!("Error: no link between {v} and {w}");
return;
}
self.forest[w].left = None;
self.forest[left].parent = Parent::Root;
self.forest[v].degree -= 1;
self.forest[w].degree -= 1;
}
}

Expand Down Expand Up @@ -516,4 +541,27 @@ mod tests {
assert_eq!(lctree.path(4, 3).sum, 15.);
assert_eq!(lctree.path(5, 7).sum, 9.);
}

#[test]
pub fn deletion() {
let mut lctree = super::LinkCutTree::default();
for i in 0..10 {
lctree.make_tree(i as f64);
}
// Check the forest size:
assert!(lctree.forest.len() == 10);

// We delete nodes 2, 4, 9:
lctree.remove_tree(2);
lctree.remove_tree(5);
lctree.remove_tree(9);

// Add 3 new nodes:
lctree.make_tree(10.0);
lctree.make_tree(11.0);
lctree.make_tree(12.0);

// Check that the space of deleted nodes are reused:
assert!(lctree.forest.len() == 10);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// #![allow(dead_code, clippy::module_name_repetitions)] // yes, I want to name my structs with the same name as the file
mod index;
mod lctree;
mod node;
mod path;
Expand Down
3 changes: 3 additions & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct Node<T: Path> {
// for path aggregation:
pub weight: f64,
pub path: T,
// for deletion (the number of edges connected to this node):
pub degree: usize,
}

impl<T: Path> Node<T> {
Expand All @@ -28,6 +30,7 @@ impl<T: Path> Node<T> {
flipped: false,
weight,
path: T::default(weight, idx),
degree: 0,
}
}

Expand Down
Loading

0 comments on commit 17055c8

Please sign in to comment.