Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link non-root nodes #23

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 66 additions & 209 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod node;
mod path;
mod splay;
use path::update_max;
use splay::unflip;

use crate::{
node::{Node, Parent},
Expand Down Expand Up @@ -52,9 +53,16 @@ impl LinkCutTree {
update_max(&mut self.forest, v);
}

/// Makes v the root of its represented tree by flipping the path from v to the root.
pub fn reroot(&mut self, v: usize) {
self.access(v);
self.forest[v].flipped ^= true;
unflip(&mut self.forest, 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.reroot(v);
self.access(w);
if !matches!(self.forest[v].parent, Parent::Root) || v == w {
return; // already connected
Expand All @@ -65,25 +73,31 @@ impl LinkCutTree {

/// 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.reroot(v); // v is now the root of the tree
self.access(w);
// if access(w) messed with the root of the tree, then v and w are connected:
!matches!(self.forest[v].parent, Parent::Root) || v == w
}

/// 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 {
/// Cuts the link between nodes v and w (if it exists)
pub fn cut(&mut self, v: usize, w: usize) {
self.reroot(v);
self.access(w);
if matches!(self.forest[v].parent, Parent::Root) || v == w {
return; // not connected
}
// detach w from its parent (which is v)
if let Some(left) = self.forest[w].left {
self.forest[w].left = None;
self.forest[left].parent = Parent::Root;
self.forest[v].left = None;
}
}

/// 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 maximum weight in the path from nodes v and w (if they are connected)
pub fn findmax(&mut self, v: usize, w: usize) -> usize {
self.reroot(v);
self.access(w);
self.forest[w].max_weight_idx
}

/// Finds the root of the tree that v is in.
Expand All @@ -93,6 +107,7 @@ impl LinkCutTree {
while let Some(left) = self.forest[root].left {
root = left;
}
splay(&mut self.forest, root); // fast access to the root next time
root
}
}
Expand All @@ -102,15 +117,7 @@ mod tests {
use crate::node::Parent;

#[test]
pub fn access_base_case() {
// access a single node, should do nothing
let mut tree = super::LinkCutTree::new(1);
tree.access(0);
assert!(matches!(tree.forest[0].parent, Parent::Root));
}

#[test]
pub fn access_leaf() {
pub fn access() {
let mut tree = super::LinkCutTree::new(4);
// '1' has a path pointer to '0', '1' has a right child '2'.
// after access(2), '2' should be the root of the tree:
Expand All @@ -134,23 +141,15 @@ mod tests {
assert_eq!(tree.forest[1].right, None);
}

#[test]
pub fn link_base_case() {
let mut tree = super::LinkCutTree::new(2);
assert!(!tree.connected(0, 1)); // not connected yet
tree.link(0, 1);
assert!(tree.connected(0, 1)); // now connected
}

#[test]
pub fn link_already_connected() {
// '2' has a right child '3':
// link(0, 3) should add no link, and result in (| denotes a path pointer):
// 0 0 0 3
// / \ / | / | /
// 1 2 => 1 2 => 1 3 => 0
// \ \ / / \
// 3 3 2 1 2
// 0 3
// / \ /
// 1 2 => 0
// \ |\
// 3 1 2
//
let mut tree = super::LinkCutTree::new(4);
tree.forest[0].left = Some(1);
Expand All @@ -164,49 +163,14 @@ mod tests {
assert_eq!(tree.forest[3].left, Some(0));
assert_eq!(tree.forest[3].right, None);
assert!(matches!(tree.forest[0].parent, Parent::Node(3)));
assert_eq!(tree.forest[0].left, Some(1));
assert_eq!(tree.forest[0].right, Some(2));
assert!(matches!(tree.forest[1].parent, Parent::Node(0)));
assert_eq!(tree.forest[1].left, None);
assert_eq!(tree.forest[1].right, None);
assert!(matches!(tree.forest[2].parent, Parent::Node(0)));
assert_eq!(tree.forest[2].left, None);
assert_eq!(tree.forest[2].right, None);
}

#[test]
pub fn link_already_connected_with_path() {
// '3' has a path pointer to '2', and '2' has a path pointer to '0':
// link(0, 3) should add no link, and result in (| denotes a path pointer):
// 0 0 0 3
// / \ / | / | /
// 1 2 => 1 2 => 1 3 => 0
// | | / / \
// 3 3 2 1 2
//
let mut tree = super::LinkCutTree::new(4);
tree.forest[0].left = Some(1);
tree.forest[0].right = Some(2);
tree.forest[1].parent = Parent::Node(0);
tree.forest[2].parent = Parent::Node(0);
tree.forest[3].parent = Parent::Path(2);
tree.link(0, 3);
assert!(matches!(tree.forest[3].parent, Parent::Root));
assert_eq!(tree.forest[3].left, Some(0));
assert_eq!(tree.forest[3].right, None);
assert!(matches!(tree.forest[0].parent, Parent::Node(3)));
assert_eq!(tree.forest[0].left, Some(1));
assert_eq!(tree.forest[0].left, None);
assert!(matches!(tree.forest[1].parent, Parent::Path(0)));
assert_eq!(tree.forest[0].right, Some(2));
assert!(matches!(tree.forest[1].parent, Parent::Node(0)));
assert_eq!(tree.forest[1].left, None);
assert_eq!(tree.forest[1].right, None);
assert!(matches!(tree.forest[2].parent, Parent::Node(0)));
assert_eq!(tree.forest[2].left, None);
assert_eq!(tree.forest[2].right, None);
}

#[test]
pub fn link_to_leftmost() {
pub fn link() {
// Given two trees:
// 0 3
// / \
Expand Down Expand Up @@ -234,27 +198,7 @@ mod tests {
}

#[test]
pub fn connected_with_root() {
// check three nodes, one is root:
// 0
// / \
// 1 2
let mut tree = super::LinkCutTree::new(3);
tree.forest[0].left = Some(1);
tree.forest[0].right = Some(2);
tree.forest[1].parent = Parent::Node(0);
tree.forest[2].parent = Parent::Node(0);

assert!(tree.connected(0, 1));
assert!(tree.connected(0, 2));
assert!(tree.connected(1, 2));
assert!(tree.connected(0, 0));
assert!(tree.connected(1, 1));
assert!(tree.connected(2, 2));
}

#[test]
pub fn connected_with_path_pointers() {
pub fn connected() {
// check two splay trees that are connected by a path pointer
// 0
// / \
Expand All @@ -274,73 +218,7 @@ mod tests {
}

#[test]
pub fn cut_base_case() {
let mut tree = super::LinkCutTree::new(2);
assert!(!tree.connected(0, 1)); // not connected yet

tree.link(0, 1);
// 0
// / <= link(0, 1)
// 1
assert!(matches!(tree.forest[0].parent, Parent::Root));
assert_eq!(tree.forest[0].left, Some(1));
assert_eq!(tree.forest[1].right, None);
assert!(matches!(tree.forest[1].parent, Parent::Node(0)));

assert!(tree.connected(0, 1)); // now connected
assert!(matches!(tree.forest[1].parent, Parent::Root));
assert_eq!(tree.forest[1].right, None);
assert_eq!(tree.forest[1].left, None);
assert!(matches!(tree.forest[0].parent, Parent::Path(1)));

tree.cut(0);
assert!(matches!(tree.forest[1].parent, Parent::Root));
assert_eq!(tree.forest[1].right, None);
// 0 0
// / =>
// 1 1
assert!(!tree.connected(0, 1)); // now disconnected
}

#[test]
pub fn cut_into_two_subtrees() {
let mut tree = super::LinkCutTree::new(5);
tree.forest[0].left = Some(1);
tree.forest[1].parent = Parent::Node(0);
tree.forest[1].left = Some(2);
tree.forest[2].parent = Parent::Node(1);
tree.forest[3].right = Some(4);
tree.forest[4].parent = Parent::Node(3);
// Given two trees:
// 0 3
// / \
// 1 4
// /
// 2
tree.link(2, 3);
// 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);
assert_eq!(tree.forest[1].right, Some(0));
assert!(matches!(tree.forest[3].parent, Parent::Node(2)));
assert_eq!(tree.forest[3].left, None);
assert_eq!(tree.forest[3].right, None);
assert!(matches!(tree.forest[4].parent, Parent::Path(3)));

// We cut node 2 from its parent 3:
tree.cut(2);
assert!(!tree.connected(2, 3));
assert!(!tree.connected(2, 4));
}

#[test]
pub fn connectivity() {
pub fn cut() {
// We form a link-cut tree from a rooted tree with the following structure:
// 0
// / \
Expand Down Expand Up @@ -370,7 +248,7 @@ mod tests {
}

// We cut node 6 from its parent 0:
lctree.cut(6);
lctree.cut(6, 0);

// The forest should now look like this:
// 0
Expand Down Expand Up @@ -401,54 +279,6 @@ mod tests {
}
}

#[test]
pub fn findmax() {
let mut lctree = super::LinkCutTree::new(10);
lctree.forest[0].weight = 4.0;
lctree.forest[1].weight = 2.0;
lctree.forest[2].weight = 6.0;
lctree.forest[3].weight = 3.0;
lctree.forest[4].weight = 9.0;
lctree.forest[5].weight = 5.0;
lctree.forest[6].weight = 0.0;
lctree.forest[7].weight = 7.0;
lctree.forest[8].weight = 1.0;
lctree.forest[9].weight = 8.0;

// 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)
// / \ \
// 2(6) 3(3) 6(0)
// / \
// 4(9) 7(7)
// / \
// 8(1) 9(8)
lctree.link(1, 0);
lctree.link(2, 1);
lctree.link(3, 1);
lctree.link(4, 2);
lctree.link(5, 0);
lctree.link(6, 5);
lctree.link(7, 6);
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:
assert_eq!(lctree.findmax(0), 0);
assert_eq!(lctree.findmax(5), 5);
assert_eq!(lctree.findmax(1), 0);
assert_eq!(lctree.findmax(8), 7);
assert_eq!(lctree.findmax(2), 2);
assert_eq!(lctree.findmax(4), 4);
assert_eq!(lctree.findmax(7), 7);
assert_eq!(lctree.findmax(9), 9);
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:
Expand Down Expand Up @@ -478,7 +308,7 @@ mod tests {
}

// We cut node 6 from its parent 0:
lctree.cut(6);
lctree.cut(6, 0);

// the forest should now look like this:
// 0
Expand All @@ -500,4 +330,31 @@ mod tests {
assert_eq!(lctree.findroot(i), 6);
}
}

#[test]
pub fn reroot() {
// We form a link-cut tree from a rooted tree with the following structure:
// 0
// / \
// 1 4
// / \
// 2 3
let mut lctree = super::LinkCutTree::new(10);
lctree.link(1, 0);
lctree.link(2, 1);
lctree.link(3, 1);
lctree.link(4, 0);

// Checking findroot (which should be 0 for all nodes):
for i in 0..5 {
assert_eq!(lctree.findroot(i), 0);
}

// we make 1 the root of the tree:
lctree.reroot(1);

for i in 0..5 {
assert_eq!(lctree.findroot(i), 1);
}
}
}
Loading