From f4dfb5353890bce60d67ef54dd45891631722acf Mon Sep 17 00:00:00 2001 From: Andrey Kuchma Date: Fri, 22 May 2026 01:23:17 +0300 Subject: [PATCH] Add splay tree data structure --- DIRECTORY.md | 1 + src/data_structures/mod.rs | 2 + src/data_structures/splay_tree.rs | 441 ++++++++++++++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 src/data_structures/splay_tree.rs diff --git a/DIRECTORY.md b/DIRECTORY.md index 9a2c4abb7d2..e3d98011ba1 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -113,6 +113,7 @@ * [RB Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/rb_tree.rs) * [Segment Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/segment_tree.rs) * [Segment Tree Recursive](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/segment_tree_recursive.rs) + * [Splay Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/splay_tree.rs) * [Stack Using Singly Linked List](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/stack_using_singly_linked_list.rs) * [Treap](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/treap.rs) * [Trie](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/trie.rs) diff --git a/src/data_structures/mod.rs b/src/data_structures/mod.rs index 19d5b1363fd..45668a9f32c 100644 --- a/src/data_structures/mod.rs +++ b/src/data_structures/mod.rs @@ -15,6 +15,7 @@ mod rb_tree; mod segment_tree; mod segment_tree_recursive; mod skip_list; +mod splay_tree; mod stack_using_singly_linked_list; mod treap; mod trie; @@ -40,6 +41,7 @@ pub use self::rb_tree::RBTree; pub use self::segment_tree::SegmentTree; pub use self::segment_tree_recursive::SegmentTree as SegmentTreeRecursive; pub use self::skip_list::SkipList; +pub use self::splay_tree::SplayTree; pub use self::stack_using_singly_linked_list::Stack; pub use self::treap::Treap; pub use self::trie::Trie; diff --git a/src/data_structures/splay_tree.rs b/src/data_structures/splay_tree.rs new file mode 100644 index 00000000000..a02a81600cd --- /dev/null +++ b/src/data_structures/splay_tree.rs @@ -0,0 +1,441 @@ +use std::cmp::Ordering; + +/// A self-adjusting binary search tree. +/// +/// Splay trees move recently accessed values to the root. They do not keep a +/// strict height-balance invariant, but provide amortized logarithmic access +/// time over a sequence of operations. +pub struct SplayTree { + root: Link, + len: usize, +} + +struct Node { + value: T, + left: Link, + right: Link, +} + +type BoxedNode = Box>; +type Link = Option>; + +enum SplayCase { + Found { + root: BoxedNode, + }, + + StopLeft { + root: BoxedNode, + }, + + StopRight { + root: BoxedNode, + }, + + Left { + root: BoxedNode, + left: BoxedNode, + }, + + LeftLeft { + root: BoxedNode, + left: BoxedNode, + }, + + LeftRight { + root: BoxedNode, + left: BoxedNode, + }, + + Right { + root: BoxedNode, + right: BoxedNode, + }, + + RightLeft { + root: BoxedNode, + right: BoxedNode, + }, + + RightRight { + root: BoxedNode, + right: BoxedNode, + }, +} + +impl SplayTree { + /// Creates an empty `SplayTree`. + pub fn new() -> Self { + Self { root: None, len: 0 } + } + + /// Returns the number of values in the tree. + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the tree contains no values. + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns `true` if the tree contains `value`. + /// + /// This operation splays the matching value to the root. If `value` is not + /// present, the last visited node is splayed to the root instead. + pub fn contains(&mut self, value: &T) -> bool { + let old_root = self.root.take(); + self.root = Self::splay(old_root, value); + + match &self.root { + Some(node) => node.value == *value, + _ => false, + } + } + + /// Inserts `value` into the tree. + /// + /// Returns `true` if the value was not already present. After the operation, + /// the inserted value, or the existing matching value, is at the root. + pub fn insert(&mut self, value: T) -> bool { + let is_contains = self.contains(&value); + + if is_contains { + return false; + } + + self.len += 1; + let mut new_root = Box::new(Node { + value, + left: None, + right: None, + }); + if let Some(mut root_node) = self.root.take() { + match new_root.value.cmp(&root_node.value) { + Ordering::Less => { + new_root.left = root_node.left.take(); + new_root.right = Some(root_node); + } + Ordering::Greater => { + new_root.right = root_node.right.take(); + new_root.left = Some(root_node); + } + Ordering::Equal => { + // unreachable + } + } + } + self.root = Some(new_root); + true + } + + /// Merges `other` into this tree. + /// + /// This method assumes that every value in this tree is less than every + /// value in `other`. If this precondition is not met, the binary search tree + /// invariant may be broken. + pub fn merge(&mut self, other: SplayTree) { + self.len += other.len(); + self.merge_with_node_root(other.root); + } + + /// Removes `value` from the tree and returns it if it was present. + /// + /// If the value is found, it is first splayed to the root, then the + /// remaining left and right subtrees are merged. + pub fn remove(&mut self, value: &T) -> Option { + let is_contains = self.contains(value); + if is_contains { + self.len -= 1; + let root = self.root.take()?; + let answer = root.value; + let left = root.left; + let right = root.right; + self.root = left; + self.merge_with_node_root(right); + Some(answer) + } else { + None + } + } + + fn merge_with_node_root(&mut self, other_root: Link) { + self.splay_max(); + + match &mut self.root { + Some(root) => root.right = other_root, + None => { + self.root = other_root; + } + } + } + + fn splay_max(&mut self) { + if let Some(root) = self.root.take() { + self.root = Some(Self::splay_max_unwrapped(root)); + } + } + + fn splay_max_unwrapped(mut root: BoxedNode) -> BoxedNode { + let Some(mut right) = root.right.take() else { + return root; + }; + + if let Some(right_right) = right.right.take() { + right.right = Some(Self::splay_max_unwrapped(right_right)); + + let new_root = Self::rotate_left_with_right(root, right); + Self::rotate_left_if_possible(new_root) + } else { + Self::rotate_left_with_right(root, right) + } + } + + fn splay(root: Link, key: &T) -> Link { + let root = root?; + Some(Self::splay_unwrapped(root, key)) + } + + fn splay_unwrapped(root: BoxedNode, key: &T) -> BoxedNode { + let class = Self::classify(root, key); + + match class { + SplayCase::Found { root } + | SplayCase::StopLeft { root } + | SplayCase::StopRight { root } => root, + SplayCase::Left { root, left } => Self::rotate_right_with_left(root, left), + SplayCase::LeftLeft { root, mut left } => { + if let Some(left_left) = left.left.take() { + left.left = Some(Self::splay_unwrapped(left_left, key)); + } + let parent_root = Self::rotate_right_with_left(root, left); + Self::rotate_right_if_possible(parent_root) + } + SplayCase::LeftRight { root, mut left } => { + if let Some(left_right) = left.right.take() { + left.right = Some(Self::splay_unwrapped(left_right, key)); + } + let new_left = Self::rotate_left_if_possible(left); + Self::rotate_right_with_left(root, new_left) + } + SplayCase::Right { root, right } => Self::rotate_left_with_right(root, right), + SplayCase::RightLeft { root, mut right } => { + if let Some(right_left) = right.left.take() { + right.left = Some(Self::splay_unwrapped(right_left, key)); + } + let new_right = Self::rotate_right_if_possible(right); + Self::rotate_left_with_right(root, new_right) + } + SplayCase::RightRight { root, mut right } => { + if let Some(right_right) = right.right.take() { + right.right = Some(Self::splay_unwrapped(right_right, key)); + } + let parent_root = Self::rotate_left_with_right(root, right); + Self::rotate_left_if_possible(parent_root) + } + } + } + + fn classify(mut root: BoxedNode, key: &T) -> SplayCase { + match key.cmp(&root.value) { + Ordering::Equal => SplayCase::Found { root }, + Ordering::Less => { + let Some(left) = root.left.take() else { + return SplayCase::StopLeft { root }; + }; + + match key.cmp(&left.value) { + Ordering::Equal => SplayCase::Left { root, left }, + Ordering::Less => SplayCase::LeftLeft { root, left }, + Ordering::Greater => SplayCase::LeftRight { root, left }, + } + } + Ordering::Greater => { + let Some(right) = root.right.take() else { + return SplayCase::StopRight { root }; + }; + + match key.cmp(&right.value) { + Ordering::Equal => SplayCase::Right { root, right }, + Ordering::Less => SplayCase::RightLeft { root, right }, + Ordering::Greater => SplayCase::RightRight { root, right }, + } + } + } + } + + fn rotate_right_with_left(mut root: BoxedNode, mut left: BoxedNode) -> BoxedNode { + root.left = left.right.take(); + left.right = Some(root); + left + } + + fn rotate_left_with_right(mut root: BoxedNode, mut right: BoxedNode) -> BoxedNode { + root.right = right.left.take(); + right.left = Some(root); + right + } + + fn rotate_right_if_possible(mut root: BoxedNode) -> BoxedNode { + let Some(left) = root.left.take() else { + return root; + }; + Self::rotate_right_with_left(root, left) + } + + fn rotate_left_if_possible(mut root: BoxedNode) -> BoxedNode { + let Some(right) = root.right.take() else { + return root; + }; + Self::rotate_left_with_right(root, right) + } +} + +impl Default for SplayTree { + fn default() -> Self { + Self::new() + } +} + +impl FromIterator for SplayTree { + fn from_iter>(iter: I) -> Self { + let mut tree = SplayTree::new(); + for value in iter { + tree.insert(value); + } + tree + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn collect_in_order<'a>(node: &'a Link, values: &mut Vec<&'a i32>) { + if let Some(node) = node { + collect_in_order(&node.left, values); + values.push(&node.value); + collect_in_order(&node.right, values); + } + } + + fn assert_bst_invariant(tree: &SplayTree) { + let mut values = Vec::new(); + collect_in_order(&tree.root, &mut values); + + assert_eq!(values.len(), tree.len()); + + for pair in values.windows(2) { + assert!( + pair[0] < pair[1], + "in-order traversal must be strictly sorted" + ); + } + } + + #[test] + fn new_tree_is_empty() { + let tree = SplayTree::::new(); + + assert_eq!(tree.len(), 0); + assert!(tree.is_empty()); + } + + #[test] + fn simple_insert_contains() { + let mut tree = SplayTree::::new(); + + tree.insert(1); + + assert!(tree.contains(&1)); + } + + #[test] + fn insert_contains() { + let mut tree: SplayTree = (0..10).collect(); + + for i in 0..10 { + assert!(!tree.insert(i)); + assert!(tree.contains(&i)); + } + + for i in -10..0 { + assert!(!tree.contains(&i)); + } + + assert_eq!(tree.len(), 10); + } + + #[test] + fn mixed_accesses_keep_recent_items_near_root() { + let mut tree = (0..5000).collect::>(); + + for _ in 0..100 { + for x in [4000, 2000, 1000, 2000, 3000, 4000] { + assert!(tree.contains(&x)); + } + } + } + + #[test] + fn merge_splay_trees() { + let mut tree1 = (0..100).collect::>(); + let tree2 = (100..200).collect::>(); + + tree1.merge(tree2); + + for i in 0..200 { + assert!(tree1.contains(&i)); + } + + assert_eq!(tree1.len(), 200); + + assert_eq!(tree1.remove(&15), Some(15)); + assert_eq!(tree1.remove(&15), None); + assert_eq!(tree1.len(), 199); + } + + #[test] + fn remove() { + let mut tree = (0..100).collect::>(); + + tree.remove(&32); + + assert!(!tree.contains(&32)); + + for i in 0..32 { + assert!(tree.contains(&i)); + } + + for i in 33..100 { + assert!(tree.contains(&i)); + } + + assert_eq!(tree.len(), 99); + } + + #[test] + fn operations_keep_bst_invariant() { + let mut tree = SplayTree::::new(); + + for value in [10, 4, 20, 2, 8, 15, 30, 6, 9] { + assert!(tree.insert(value)); + assert_bst_invariant(&tree); + } + + for value in [6, 30, 10, 3, 21] { + tree.contains(&value); + assert_bst_invariant(&tree); + } + + assert_eq!(tree.remove(&8), Some(8)); + assert_bst_invariant(&tree); + + assert_eq!(tree.remove(&100), None); + assert_bst_invariant(&tree); + + let right_tree = [35, 40, 45, 50].into_iter().collect::>(); + tree.merge(right_tree); + assert_bst_invariant(&tree); + } +}