From 210544e3d9eb1778e3bb1c25259a2161ff5a5a28 Mon Sep 17 00:00:00 2001 From: Aziz Kayumov Date: Fri, 6 Oct 2023 12:51:04 +0900 Subject: [PATCH] closes #18, closes #20 --- src/lib.rs | 90 +++++++++++-- src/splay.rs | 269 +++++++++++++++++++------------------ tests/test_connectivity.rs | 2 + 3 files changed, 219 insertions(+), 142 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9891a0e..404bdbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ impl LinkCutTree { self.forest[v].weight = weight; } - // Constructs a path from a node to the root of the tree. + /// Constructs a path from a node to the root of the tree. pub fn access(&mut self, v: usize) { splay(&mut self.forest, v); @@ -50,7 +50,7 @@ impl LinkCutTree { update_max(&mut self.forest, v); } - /// Creates a link between two nodes in the forest (where w is the parent of v) + /// Creates a link between two nodes in the forest (where w is the parent of v). pub fn link(&mut self, v: usize, w: usize) { self.access(v); self.access(w); @@ -61,7 +61,7 @@ impl LinkCutTree { self.forest[w].parent = Parent::Node(v); } - // Checks if v and w are connected in the forest + /// Checks if v and w are connected in the forest. pub fn connected(&mut self, v: usize, w: usize) -> bool { self.access(v); // v is now the root of the tree self.access(w); @@ -69,7 +69,7 @@ impl LinkCutTree { !matches!(self.forest[v].parent, Parent::Root) || v == w } - // Cuts the link between v and its parent. + /// Cuts the link between v and its parent. pub fn cut(&mut self, v: usize) { self.access(v); if let Some(left) = self.forest[v].left { @@ -78,11 +78,21 @@ impl LinkCutTree { } } - // Finds the maximum weight in the path from v and its parent + /// Finds the maximum weight in the path from v and the root of the tree that v is in. pub fn findmax(&mut self, v: usize) -> usize { self.access(v); self.forest[v].max_weight_idx } + + /// Finds the root of the tree that v is in. + pub fn findroot(&mut self, v: usize) -> usize { + self.access(v); + let mut root = v; + while let Some(left) = self.forest[root].left { + root = left; + } + root + } } #[cfg(test)] @@ -306,13 +316,12 @@ mod tests { // / // 2 tree.link(2, 3); - // link(2, 3) should result in: + // link(2, 3) should now result in: // 2 // / | // 3 1 // | \ // 4 0 - // assert!(matches!(tree.forest[2].parent, Parent::Root)); assert_eq!(tree.forest[2].left, Some(3)); assert_eq!(tree.forest[2].right, None); @@ -322,7 +331,7 @@ mod tests { assert_eq!(tree.forest[3].right, None); assert!(matches!(tree.forest[4].parent, Parent::Path(3))); - // we cut node 2 from its parent 3: + // We cut node 2 from its parent 3: tree.cut(2); assert!(!tree.connected(2, 3)); assert!(!tree.connected(2, 4)); @@ -351,17 +360,17 @@ mod tests { lctree.link(8, 7); lctree.link(9, 8); - // checking connectivity: + // Checking connectivity: for i in 0..10 { for j in 0..10 { assert!(lctree.connected(i, j)); } } - // we cut node 6 from its parent 0: + // We cut node 6 from its parent 0: lctree.cut(6); - // the forest should now look like this: + // The forest should now look like this: // 0 // / // 1 6 @@ -372,7 +381,7 @@ mod tests { // / // 9 - // we check connectivity again for the two trees: + // We check connectivity again for the two trees: for i in 0..6 { for j in 0..6 { assert!(lctree.connected(i, j)); @@ -404,7 +413,8 @@ mod tests { lctree.forest[8].weight = 1.0; lctree.forest[9].weight = 8.0; - // we form the following structure: + // We form a link-cut tree from a rooted tree with the following structure + // (the numbers in parentheses are the weights of the nodes): // 0(4) // / \ // 1(2) 5(5) @@ -424,7 +434,7 @@ mod tests { lctree.link(8, 7); lctree.link(9, 7); - // we check the node index with max weight in the path from each node to the root: + // We check the node index with max weight in the path from each node to the root: assert_eq!(lctree.findmax(0), 0); assert_eq!(lctree.findmax(5), 5); assert_eq!(lctree.findmax(1), 0); @@ -436,4 +446,56 @@ mod tests { assert_eq!(lctree.findmax(3), 0); assert_eq!(lctree.findmax(6), 5); } + + #[test] + pub fn findroot() { + // We form a link-cut tree from a rooted tree with the following structure: + // 0 + // / \ + // 1 6 + // / \ \ + // 2 3 7 + // / \ \ + // 4 5 8 + // / + // 9 + let mut lctree = super::LinkCutTree::new(10); + lctree.link(1, 0); + lctree.link(2, 1); + lctree.link(3, 1); + lctree.link(4, 3); + lctree.link(5, 3); + lctree.link(6, 0); + lctree.link(7, 6); + lctree.link(8, 7); + lctree.link(9, 8); + + // Checking findroot: + for i in 0..10 { + assert_eq!(lctree.findroot(i), 0); + } + + // We cut node 6 from its parent 0: + lctree.cut(6); + + // the forest should now look like this: + // 0 + // / + // 1 6 + // / \ \ + // 2 3 7 + // / \ \ + // 4 5 8 + // / + // 9 + + // We check findroot again for the two trees: + for i in 0..6 { + assert_eq!(lctree.findroot(i), 0); + } + + for i in 6..10 { + assert_eq!(lctree.findroot(i), 6); + } + } } diff --git a/src/splay.rs b/src/splay.rs index e23d4cb..1827e8f 100644 --- a/src/splay.rs +++ b/src/splay.rs @@ -1,12 +1,23 @@ use crate::node::{Node, Parent}; +/// Rotates the subtree rooted at `node_idx` to the left: +/// +/// # Panics +/// Panics if `node_idx` is out of bounds or if `node_idx` does not have a right child. +/// +/// # Examples: +/// 0 1 +/// \ => / +/// 1 0 fn rotate_left(forest: &mut [Node], node_idx: usize) { - // base cases: - // - node_idx is out of bounds - // - node_idx should have a right child - if forest[node_idx].right.is_none() { - return; - } + assert!( + node_idx < forest.len(), + "rotate_left: node_idx out of bounds" + ); + assert!( + forest[node_idx].right.is_some(), + "rotate_left: node_idx does not have a right child" + ); let right_child = forest[node_idx].right.unwrap(); if let Parent::Node(parent_idx) = forest[node_idx].parent { @@ -31,13 +42,24 @@ fn rotate_left(forest: &mut [Node], node_idx: usize) { update_max(forest, right_child); } +/// Rotates the subtree rooted at `node_idx` to the right: +/// +/// # Panics +/// Panics if `node_idx` is out of bounds or if `node_idx` does not have a left child. +/// +/// # Examples: +/// 0 1 +/// / => \ +/// 1 0 fn rotate_right(forest: &mut [Node], node_idx: usize) { - // base cases: - // - node_idx is out of bounds - // - node_idx should have a left child - if forest[node_idx].left.is_none() { - return; - } + assert!( + node_idx < forest.len(), + "rotate_right: node_idx out of bounds" + ); + assert!( + forest[node_idx].left.is_some(), + "rotate_right: node_idx does not have a left child" + ); let left_child = forest[node_idx].left.unwrap(); if let Parent::Node(parent_idx) = forest[node_idx].parent { @@ -79,9 +101,18 @@ pub fn update_max(forest: &mut [Node], node_idx: usize) { forest[node_idx].max_weight_idx = max_idx; } -// makes node_idx the root of its Splay tree +/// Splays the subtree rooted at `node_idx`, making it the new root of the tree. +/// +/// # Examples: +/// splaying on '2': +/// 0 2 +/// \ => / \ +/// 1 0 1 +/// / +/// 2 pub fn splay(forest: &mut [Node], node_idx: usize) { assert!(node_idx < forest.len(), "splay: node_idx out of bounds"); + while let Parent::Node(parent_idx) = forest[node_idx].parent { if forest[parent_idx].left == Some(node_idx) { if let Parent::Node(grandparent_idx) = forest[parent_idx].parent { @@ -92,7 +123,7 @@ pub fn splay(forest: &mut [Node], node_idx: usize) { } else { // zig-zag rotate_right(forest, parent_idx); - rotate_left(forest, parent_idx); + rotate_left(forest, grandparent_idx); } } else { // zig @@ -106,7 +137,7 @@ pub fn splay(forest: &mut [Node], node_idx: usize) { } else { // zig-zag rotate_left(forest, parent_idx); - rotate_right(forest, parent_idx); + rotate_right(forest, grandparent_idx); } } else { // zig @@ -125,66 +156,42 @@ mod tests { } #[test] - pub fn rotate_left_base_case() { - // rotate left a single node, should do nothing: + #[should_panic] + pub fn rotate_left_single_node() { + // rotate left a single node, should panic: let mut forest = create_nodes(1); rotate_left(&mut forest, 0); - assert_eq!(forest[0].right, None); - assert!(forest[0].right.is_none()); - assert!(matches!(forest[0].parent, node::Parent::Root)); } #[test] - pub fn rotate_left_two_nodes() { + pub fn rotate_left_with_parent() { // connect two nodes and rotate left on '0': - // 0 1 - // \ => / - // 1 0 - let mut forest = create_nodes(2); - forest[0].right = Some(1); + // 0 2 + // / \ => / + // 1 2 0 + // / + // 1 + let mut forest = create_nodes(3); + forest[0].left = Some(1); + forest[0].right = Some(2); forest[1].parent = node::Parent::Node(0); + forest[2].parent = node::Parent::Node(0); rotate_left(&mut forest, 0); - assert_eq!(forest[1].left, Some(0)); + assert!(matches!(forest[2].parent, node::Parent::Root)); + assert_eq!(forest[2].left, Some(0)); + assert_eq!(forest[2].right, None); + assert!(matches!(forest[0].parent, node::Parent::Node(2))); + assert_eq!(forest[0].left, Some(1)); assert_eq!(forest[0].right, None); - assert_eq!(forest[0].left, None); - assert!(matches!(forest[0].parent, node::Parent::Node(1))); - assert!(matches!(forest[1].parent, node::Parent::Root)); + assert!(matches!(forest[1].parent, node::Parent::Node(0))); + assert!(forest[1].left.is_none()); + assert!(forest[1].right.is_none()); } #[test] - pub fn rotate_left_with_parent() { - // rotate left '1': - // 0 0 - // \ \ - // 1 => 2 - // \ / \ - // 2 1 4 - // / \ \ - // 3 4 3 - let mut forest = create_nodes(5); - forest[0].right = Some(1); - forest[1].parent = node::Parent::Node(0); - forest[1].right = Some(2); - forest[2].parent = node::Parent::Node(1); - forest[2].left = Some(3); - forest[3].parent = node::Parent::Node(2); - forest[2].right = Some(4); - forest[4].parent = node::Parent::Node(2); - rotate_left(&mut forest, 1); - assert!(matches!(forest[0].parent, node::Parent::Root)); - assert_eq!(forest[0].right, Some(2)); - assert!(matches!(forest[2].parent, node::Parent::Node(0))); - assert_eq!(forest[2].left, Some(1)); - assert!(matches!(forest[1].parent, node::Parent::Node(2))); - assert_eq!(forest[2].right, Some(4)); - assert!(matches!(forest[4].parent, node::Parent::Node(2))); - assert_eq!(forest[1].right, Some(3)); - assert!(matches!(forest[3].parent, node::Parent::Node(1))); - } - - #[test] - pub fn rotate_right_base_case() { - // rotate right a single node, should do nothing: + #[should_panic] + pub fn rotate_right_single_node() { + // rotate right a single node, should panic: let mut forest = create_nodes(1); rotate_right(&mut forest, 0); assert!(forest[0].left.is_none()); @@ -193,54 +200,32 @@ mod tests { } #[test] - pub fn rotate_right_two_nodes() { - // connect two nodes and rotate right on '0': - // 0 1 - // / => \ - // 1 0 - let mut forest = create_nodes(2); + pub fn rotate_right_with_parent() { + // connect two nodes and rotate left on '0': + // 0 1 + // / \ => \ + // 1 2 0 + // \ + // 2 + let mut forest = create_nodes(3); forest[0].left = Some(1); + forest[0].right = Some(2); forest[1].parent = node::Parent::Node(0); + forest[2].parent = node::Parent::Node(0); rotate_right(&mut forest, 0); - assert_eq!(forest[0].left, None); + assert!(matches!(forest[1].parent, node::Parent::Root)); + assert_eq!(forest[1].left, None); assert_eq!(forest[1].right, Some(0)); assert!(matches!(forest[0].parent, node::Parent::Node(1))); - assert!(matches!(forest[1].parent, node::Parent::Root)); - } - - #[test] - pub fn rotate_right_with_parent() { - // rotate right on '1': - // 0 0 - // \ \ - // 1 => 2 - // / / \ - // 2 3 1 - // / \ / - // 3 4 4 - let mut forest = create_nodes(5); - forest[0].right = Some(1); - forest[1].parent = node::Parent::Node(0); - forest[1].left = Some(2); - forest[2].parent = node::Parent::Node(1); - forest[2].left = Some(3); - forest[3].parent = node::Parent::Node(2); - forest[2].right = Some(4); - forest[4].parent = node::Parent::Node(2); - rotate_right(&mut forest, 1); - assert!(matches!(forest[0].parent, node::Parent::Root)); + assert_eq!(forest[0].left, None); assert_eq!(forest[0].right, Some(2)); assert!(matches!(forest[2].parent, node::Parent::Node(0))); - assert_eq!(forest[2].left, Some(3)); - assert!(matches!(forest[3].parent, node::Parent::Node(2))); - assert_eq!(forest[2].right, Some(1)); - assert!(matches!(forest[1].parent, node::Parent::Node(2))); - assert_eq!(forest[1].left, Some(4)); - assert!(matches!(forest[4].parent, node::Parent::Node(1))); + assert!(forest[2].left.is_none()); + assert!(forest[2].right.is_none()); } #[test] - pub fn splay_base_case() { + pub fn splay_single_node() { // splay a single node, should do nothing: let mut forest = create_nodes(1); super::splay(&mut forest, 0); @@ -250,39 +235,66 @@ mod tests { } #[test] - pub fn splay_two_nodes() { - // connect two nodes and splay on '1': - // 0 1 - // \ => / - // 1 0 - let mut forest = create_nodes(2); + pub fn splay_leaf() { + // form the tree and splay on '2': + // 0 2 + // \ => / \ + // 1 0 1 + // / + // 2 + let mut forest = create_nodes(3); + forest[0].right = Some(1); + forest[1].parent = node::Parent::Node(0); + forest[1].left = Some(2); + forest[2].parent = node::Parent::Node(1); + super::splay(&mut forest, 2); + assert!(matches!(forest[2].parent, node::Parent::Root)); + assert_eq!(forest[2].left, Some(0)); + assert_eq!(forest[2].right, Some(1)); + assert!(matches!(forest[0].parent, node::Parent::Node(2))); + assert!(matches!(forest[1].parent, node::Parent::Node(2))); + } + + #[test] + pub fn splay_internal_node() { + // form the tree and splay on '1': + // 0 1 + // \ => / + // 1 0 + // / \ + // 2 2 + let mut forest = create_nodes(3); forest[0].right = Some(1); forest[1].parent = node::Parent::Node(0); + forest[1].left = Some(2); + forest[2].parent = node::Parent::Node(1); super::splay(&mut forest, 1); - assert_eq!(forest[0].right, None); - assert_eq!(forest[0].left, None); + assert!(matches!(forest[1].parent, node::Parent::Root)); assert_eq!(forest[1].left, Some(0)); assert_eq!(forest[1].right, None); assert!(matches!(forest[0].parent, node::Parent::Node(1))); - assert!(matches!(forest[1].parent, node::Parent::Root)); + assert_eq!(forest[0].left, None); + assert_eq!(forest[0].right, Some(2)); + assert!(matches!(forest[2].parent, node::Parent::Node(0))); + assert!(forest[2].left.is_none()); + assert!(forest[2].right.is_none()); } #[test] - pub fn splay_leaf() { + pub fn splay_preserve_path_pointer() { // Node '0' has a path pointer to Node '6', - // the remaning nodes are represented in a Splay-tree. + // the remaning nodes are represented in a Splay-tree as given below. // splaying a leaf node '4' should result in: - // 6 6 6 6 - // | | | | - // 0 0 0 4 - // \ \ \ / \ - // 1 => 1 => 4 => 0 1 - // / / / \ \ - // 2 4 3 1 3 - // / \ / \ \ - // 3 4 3 2 2 - // \ - // 2 + // 6 6 6 + // | | | + // 0 0 4 + // \ \ / \ + // 1 => 4 => 0 1 + // / / \ \ + // 2 2 1 2 + // / \ / / + // 3 4 3 3 + // let mut forest = create_nodes(6); forest[0].parent = node::Parent::Path(6); forest[0].right = Some(1); @@ -294,15 +306,16 @@ mod tests { forest[2].right = Some(4); forest[4].parent = node::Parent::Node(2); super::splay(&mut forest, 4); + // The path pointer to Node '6' should be preserved: assert!(matches!(forest[4].parent, node::Parent::Path(6))); + // The rest of the tree should be a rotated Splay-tree: assert_eq!(forest[4].left, Some(0)); assert!(matches!(forest[0].parent, node::Parent::Node(4))); assert_eq!(forest[4].right, Some(1)); assert!(matches!(forest[1].parent, node::Parent::Node(4))); - - assert_eq!(forest[0].right, Some(3)); - assert!(matches!(forest[3].parent, node::Parent::Node(0))); - assert_eq!(forest[3].right, Some(2)); - assert!(matches!(forest[2].parent, node::Parent::Node(3))); + assert_eq!(forest[0].right, Some(2)); + assert!(matches!(forest[2].parent, node::Parent::Node(0))); + assert_eq!(forest[2].left, Some(3)); + assert!(matches!(forest[3].parent, node::Parent::Node(2))); } } diff --git a/tests/test_connectivity.rs b/tests/test_connectivity.rs index e15967e..562686d 100644 --- a/tests/test_connectivity.rs +++ b/tests/test_connectivity.rs @@ -69,6 +69,7 @@ enum Operation { #[test] pub fn connectivity() { + let now = std::time::Instant::now(); let mut rng = create_random_generator(); let edges = create_random_tree(&mut rng); @@ -109,4 +110,5 @@ pub fn connectivity() { } } } + println!("Time: {}ms", now.elapsed().as_millis()); // Time: 40ms }