move bst into separate module
This commit is contained in:
parent
bcf9a3db19
commit
64bf67e67d
2 changed files with 240 additions and 236 deletions
237
libtree/src/binary_search_tree.rs
Normal file
237
libtree/src/binary_search_tree.rs
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
use core::fmt::Display;
|
||||||
|
use crate::Stats;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BinarySearchTreeNode {
|
||||||
|
pub key: u64,
|
||||||
|
pub left: BST,
|
||||||
|
pub right: BST,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinarySearchTreeNode {
|
||||||
|
pub fn new(key: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
key,
|
||||||
|
left: BST(None),
|
||||||
|
right: BST(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
match (&self.left.0, &self.right.0) {
|
||||||
|
(None, None) =>
|
||||||
|
1,
|
||||||
|
(None, Some(right)) =>
|
||||||
|
1 + right.height(),
|
||||||
|
(Some(left), None) =>
|
||||||
|
1 + left.height(),
|
||||||
|
(Some(left), Some(right)) =>
|
||||||
|
1 + (left.height()).max(right.height()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/66661638
|
||||||
|
fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) {
|
||||||
|
let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() };
|
||||||
|
assert!(idx < s_bytes.len());
|
||||||
|
assert!(s_bytes[idx].is_ascii());
|
||||||
|
assert!(newchar.is_ascii());
|
||||||
|
// we've made sure this is safe.
|
||||||
|
s_bytes[idx] = newchar as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, prefix: char, left_trace: &mut String, right_trace: &mut String) -> std::fmt::Result {
|
||||||
|
if let Some(left) = &self.left.0 {
|
||||||
|
left._print(f, depth + 1, '/', left_trace, right_trace)?;
|
||||||
|
}
|
||||||
|
if prefix == '/' {
|
||||||
|
Self::replace_nth_char_ascii(left_trace, depth - 1, '|');
|
||||||
|
}
|
||||||
|
if prefix == '\\' {
|
||||||
|
Self::replace_nth_char_ascii(right_trace, depth - 1, ' ');
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
write!(f, "-")?;
|
||||||
|
}
|
||||||
|
if depth > 0 {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if depth > 0 {
|
||||||
|
for i in 0..(depth - 1) {
|
||||||
|
if left_trace.chars().nth(i).unwrap() == '|' || right_trace.chars().nth(i).unwrap() == '|' {
|
||||||
|
write!(f, "| ")?;
|
||||||
|
} else {
|
||||||
|
write!(f, " ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "{}-", prefix)?;
|
||||||
|
}
|
||||||
|
writeln!(f, "[{}]", self.key)?;
|
||||||
|
Self::replace_nth_char_ascii(left_trace, depth, ' ');
|
||||||
|
|
||||||
|
if let Some(right) = &self.right.0 {
|
||||||
|
Self::replace_nth_char_ascii(right_trace, depth, '|');
|
||||||
|
right._print(f, depth + 1, '\\', left_trace, right_trace)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BST(Option<Box<BinarySearchTreeNode>>);
|
||||||
|
|
||||||
|
impl BST {
|
||||||
|
pub fn insert(&mut self, key: u64) -> (bool, Stats) {
|
||||||
|
let mut stats = Stats::new();
|
||||||
|
|
||||||
|
if self.0.is_none() {
|
||||||
|
stats.writes += 1;
|
||||||
|
self.0 = Some(Box::new(BinarySearchTreeNode::new(key)));
|
||||||
|
return (true, stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current = self;
|
||||||
|
while let Some(ref mut node) = current.0 {
|
||||||
|
stats.reads += 1;
|
||||||
|
use std::cmp::Ordering::*;
|
||||||
|
stats.comparisons += 1;
|
||||||
|
match node.key.cmp(&key) {
|
||||||
|
Greater => {
|
||||||
|
stats.reads += 1;
|
||||||
|
current = &mut node.left;
|
||||||
|
},
|
||||||
|
Less => {
|
||||||
|
stats.reads += 1;
|
||||||
|
current = &mut node.right;
|
||||||
|
},
|
||||||
|
Equal => return (false, stats),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.writes += 1;
|
||||||
|
current.0 = Some(Box::new(BinarySearchTreeNode::new(key)));
|
||||||
|
|
||||||
|
(true, stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, key: u64) -> (bool, Stats) {
|
||||||
|
let mut current: *mut BST = self;
|
||||||
|
|
||||||
|
let mut stats = Stats::default();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
while let Some(ref mut node) = (*current).0 {
|
||||||
|
stats.reads += 1;
|
||||||
|
use std::cmp::Ordering::*;
|
||||||
|
stats.comparisons += 1;
|
||||||
|
match node.key.cmp(&key) {
|
||||||
|
Greater => {
|
||||||
|
stats.reads += 1;
|
||||||
|
current = &mut node.left;
|
||||||
|
},
|
||||||
|
Less => {
|
||||||
|
stats.reads += 1;
|
||||||
|
current = &mut node.right;
|
||||||
|
},
|
||||||
|
Equal => {
|
||||||
|
match (node.left.0.as_mut(), node.right.0.as_mut()) {
|
||||||
|
(None, None) => {
|
||||||
|
stats.writes += 1;
|
||||||
|
(*current).0 = None;
|
||||||
|
},
|
||||||
|
(None, Some(_)) => {
|
||||||
|
stats.writes += 1;
|
||||||
|
(*current).0 = node.right.0.take();
|
||||||
|
},
|
||||||
|
(Some(_), None) => {
|
||||||
|
stats.writes += 1;
|
||||||
|
(*current).0 = node.left.0.take();
|
||||||
|
},
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
(*current).0.as_mut().unwrap().key = {
|
||||||
|
let mut to_return = None;
|
||||||
|
|
||||||
|
stats.reads += 1;
|
||||||
|
if node.right.0.is_some() {
|
||||||
|
stats.reads += 1;
|
||||||
|
let mut current = &mut node.right;
|
||||||
|
|
||||||
|
stats.reads += 1;
|
||||||
|
while current.0.as_ref().unwrap().left.0.is_some() {
|
||||||
|
stats.writes += 1;
|
||||||
|
current = &mut current.0.as_mut().unwrap().left;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = current.0.take().unwrap();
|
||||||
|
to_return = Some(node.key);
|
||||||
|
stats.writes += 1;
|
||||||
|
current.0 = node.right.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to_return.unwrap()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return (true, stats);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(false, stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BinarySearchTree {
|
||||||
|
pub root: BST,
|
||||||
|
pub size: usize,
|
||||||
|
print_capacity: usize,
|
||||||
|
pub stats: Stats,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinarySearchTree {
|
||||||
|
pub fn new(print_capacity: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
root: BST(None),
|
||||||
|
size: 0,
|
||||||
|
print_capacity,
|
||||||
|
stats: Stats::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
match &self.root.0 {
|
||||||
|
Some(root) => root.height(),
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, key: u64) -> bool {
|
||||||
|
let (ret, stats) = self.root.insert(key);
|
||||||
|
self.stats += stats;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, key: u64) -> bool {
|
||||||
|
let (ret, stats) = self.root.delete(key);
|
||||||
|
self.stats += stats;
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_stats(&mut self) {
|
||||||
|
self.stats = Stats::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BinarySearchTree {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self.root.0 {
|
||||||
|
Some(root) => root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
use std::fmt::Display;
|
mod binary_search_tree;
|
||||||
|
|
||||||
|
pub use binary_search_tree::*;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
|
@ -24,238 +26,3 @@ impl std::ops::AddAssign for Stats {
|
||||||
self.writes += rhs.writes;
|
self.writes += rhs.writes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Node {
|
|
||||||
pub key: u64,
|
|
||||||
pub left: Tree,
|
|
||||||
pub right: Tree,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
pub fn new(key: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
key,
|
|
||||||
left: Tree(None),
|
|
||||||
right: Tree(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height(&self) -> usize {
|
|
||||||
match (&self.left.0, &self.right.0) {
|
|
||||||
(None, None) =>
|
|
||||||
1,
|
|
||||||
(None, Some(right)) =>
|
|
||||||
1 + right.height(),
|
|
||||||
(Some(left), None) =>
|
|
||||||
1 + left.height(),
|
|
||||||
(Some(left), Some(right)) =>
|
|
||||||
1 + (left.height()).max(right.height()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/66661638
|
|
||||||
fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) {
|
|
||||||
let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() };
|
|
||||||
assert!(idx < s_bytes.len());
|
|
||||||
assert!(s_bytes[idx].is_ascii());
|
|
||||||
assert!(newchar.is_ascii());
|
|
||||||
// we've made sure this is safe.
|
|
||||||
s_bytes[idx] = newchar as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _print(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, prefix: char, left_trace: &mut String, right_trace: &mut String) -> std::fmt::Result {
|
|
||||||
if let Some(left) = &self.left.0 {
|
|
||||||
left._print(f, depth + 1, '/', left_trace, right_trace)?;
|
|
||||||
}
|
|
||||||
if prefix == '/' {
|
|
||||||
Self::replace_nth_char_ascii(left_trace, depth - 1, '|');
|
|
||||||
}
|
|
||||||
if prefix == '\\' {
|
|
||||||
Self::replace_nth_char_ascii(right_trace, depth - 1, ' ');
|
|
||||||
}
|
|
||||||
if depth == 0 {
|
|
||||||
write!(f, "-")?;
|
|
||||||
}
|
|
||||||
if depth > 0 {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if depth > 0 {
|
|
||||||
for i in 0..(depth - 1) {
|
|
||||||
if left_trace.chars().nth(i).unwrap() == '|' || right_trace.chars().nth(i).unwrap() == '|' {
|
|
||||||
write!(f, "| ")?;
|
|
||||||
} else {
|
|
||||||
write!(f, " ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(f, "{}-", prefix)?;
|
|
||||||
}
|
|
||||||
writeln!(f, "[{}]", self.key)?;
|
|
||||||
Self::replace_nth_char_ascii(left_trace, depth, ' ');
|
|
||||||
|
|
||||||
if let Some(right) = &self.right.0 {
|
|
||||||
Self::replace_nth_char_ascii(right_trace, depth, '|');
|
|
||||||
right._print(f, depth + 1, '\\', left_trace, right_trace)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Tree(Option<Box<Node>>);
|
|
||||||
|
|
||||||
impl Tree {
|
|
||||||
pub fn insert(&mut self, key: u64) -> (bool, Stats) {
|
|
||||||
let mut stats = Stats::new();
|
|
||||||
|
|
||||||
if self.0.is_none() {
|
|
||||||
stats.writes += 1;
|
|
||||||
self.0 = Some(Box::new(Node::new(key)));
|
|
||||||
return (true, stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut current = self;
|
|
||||||
while let Some(ref mut node) = current.0 {
|
|
||||||
stats.reads += 1;
|
|
||||||
use std::cmp::Ordering::*;
|
|
||||||
stats.comparisons += 1;
|
|
||||||
match node.key.cmp(&key) {
|
|
||||||
Greater => {
|
|
||||||
stats.reads += 1;
|
|
||||||
current = &mut node.left;
|
|
||||||
},
|
|
||||||
Less => {
|
|
||||||
stats.reads += 1;
|
|
||||||
current = &mut node.right;
|
|
||||||
},
|
|
||||||
Equal => return (false, stats),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.writes += 1;
|
|
||||||
current.0 = Some(Box::new(Node::new(key)));
|
|
||||||
|
|
||||||
(true, stats)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self, key: u64) -> (bool, Stats) {
|
|
||||||
let mut current: *mut Tree = self;
|
|
||||||
|
|
||||||
let mut stats = Stats::default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
while let Some(ref mut node) = (*current).0 {
|
|
||||||
stats.reads += 1;
|
|
||||||
use std::cmp::Ordering::*;
|
|
||||||
stats.comparisons += 1;
|
|
||||||
match node.key.cmp(&key) {
|
|
||||||
Greater => {
|
|
||||||
stats.reads += 1;
|
|
||||||
current = &mut node.left;
|
|
||||||
},
|
|
||||||
Less => {
|
|
||||||
stats.reads += 1;
|
|
||||||
current = &mut node.right;
|
|
||||||
},
|
|
||||||
Equal => {
|
|
||||||
match (node.left.0.as_mut(), node.right.0.as_mut()) {
|
|
||||||
(None, None) => {
|
|
||||||
stats.writes += 1;
|
|
||||||
(*current).0 = None;
|
|
||||||
},
|
|
||||||
(None, Some(_)) => {
|
|
||||||
stats.writes += 1;
|
|
||||||
(*current).0 = node.right.0.take();
|
|
||||||
},
|
|
||||||
(Some(_), None) => {
|
|
||||||
stats.writes += 1;
|
|
||||||
(*current).0 = node.left.0.take();
|
|
||||||
},
|
|
||||||
(Some(_), Some(_)) => {
|
|
||||||
(*current).0.as_mut().unwrap().key = {
|
|
||||||
let mut to_return = None;
|
|
||||||
|
|
||||||
stats.reads += 1;
|
|
||||||
if node.right.0.is_some() {
|
|
||||||
stats.reads += 1;
|
|
||||||
let mut current = &mut node.right;
|
|
||||||
|
|
||||||
stats.reads += 1;
|
|
||||||
while current.0.as_ref().unwrap().left.0.is_some() {
|
|
||||||
stats.writes += 1;
|
|
||||||
current = &mut current.0.as_mut().unwrap().left;
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = current.0.take().unwrap();
|
|
||||||
to_return = Some(node.key);
|
|
||||||
stats.writes += 1;
|
|
||||||
current.0 = node.right.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
to_return.unwrap()
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return (true, stats);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(false, stats)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BinarySearchTree {
|
|
||||||
pub root: Tree,
|
|
||||||
pub size: usize,
|
|
||||||
print_capacity: usize,
|
|
||||||
pub stats: Stats,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinarySearchTree {
|
|
||||||
pub fn new(print_capacity: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
root: Tree(None),
|
|
||||||
size: 0,
|
|
||||||
print_capacity,
|
|
||||||
stats: Stats::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height(&self) -> usize {
|
|
||||||
match &self.root.0 {
|
|
||||||
Some(root) => root.height(),
|
|
||||||
None => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, key: u64) -> bool {
|
|
||||||
let (ret, stats) = self.root.insert(key);
|
|
||||||
self.stats += stats;
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(&mut self, key: u64) -> bool {
|
|
||||||
let (ret, stats) = self.root.delete(key);
|
|
||||||
self.stats += stats;
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset_stats(&mut self) {
|
|
||||||
self.stats = Stats::default();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for BinarySearchTree {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match &self.root.0 {
|
|
||||||
Some(root) => root._print(f, 0, '-', &mut " ".repeat(self.print_capacity), &mut " ".repeat(self.print_capacity)),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue