Skip to content

Commit

Permalink
(wip) add avl tree node
Browse files Browse the repository at this point in the history
  • Loading branch information
ielm committed Mar 9, 2024
1 parent ea6bc5b commit 9cce056
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ homepage = "https://github.com/ielm/sitka"
repository = "https://github.com/ielm/sitka"
description = "A set of tree data structures for Rust"

[toolchain]
channel = "nightly"

[dependencies]

6 changes: 6 additions & 0 deletions src/avl_tree/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod node;
pub mod tree;

pub mod constants {
pub const BF: i32 = 2;
}
199 changes: 199 additions & 0 deletions src/avl_tree/node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// This is an attempt at an implementation for an AVL tree:
//
// ```rust
// struct AvlTree<K, V> {
// height: usize,
// root: Option<Box<Node<K, V, height>>>
// }
//
// struct Node<K, V, height: usize> {
// key: K,
// val: V,
// parent: Option<(NonNull<Node<K, V, height + 1>>, u16)>
// left: Option<(NonNull<Node<K, V, height - 1>>, u16)>
// right: Option<(NonNull<Node<K, V, height - 1>>, u16)>
// height: usize
// }
// ```
//
// This implementation of an AVL Tree borrows heavily from the rust
// alloc::collections::btree implementation, but contains significant differences.
// In particular, this AVL Tree does not store arrays of keys and values,
// instead preferring that each node contains a single value.
//
// TODO(@ielm)
// [ ] - Node should be renamed to InternalNode
// [ ] - Create a LeadNode variant
// [ ] - Make InternalNode & NodeLeaf private
// [ ] - Create a public NodeRef

#![allow(dead_code)]
#![allow(clippy::option_map_unit_fn)]

use std::cmp::Ordering;
use std::ptr::NonNull;

/// `OptNode` is a type alias for `Option<NonNull<Node<K, V>>>`.
///
/// It represents an optional reference to a `Node` in the AVL tree, where `None`
/// indicates the absence of a node, and `Some(NonNull<Node<K, V>>)` represents
/// a non-null pointer to a `Node` instance.
///
/// The `NonNull` wrapper ensures that the pointer is always non-null.
///
/// # Type Parameters
///
/// - `K`: The type of the keys in the AVL tree. It must implement the `Ord` trait.
/// - `V`: The type of the values associated with the keys in the AVL tree.
pub type OptNode<K, V> = Option<NonNull<Node<K, V>>>;

/// `Node` represents a node in the AVL tree.
///
/// Each node contains a key of type `K`, a value of type `V`, and references to
/// its parent node, left child, and right child. The node also stores its height,
/// which is used to maintain the balance of the AVL tree.
///
/// # Type Parameters
///
/// - `K`: The type of the key stored in the node. It must implement the `Ord` trait
/// for comparison.
/// - `V`: The type of the value associated with the key in the node.
///
/// # Fields
///
/// - `key`: The key stored in the node, of type `K`.
/// - `value`: The value associated with the key, of type `V`.
/// - `parent`: An optional reference to the parent node, represented by `OptNode<K, V>`.
/// - `left`: An optional reference to the left child node, represented by `OptNode<K, V>`.
/// - `right`: An optional reference to the right child node, represented by `OptNode<K, V>`.
/// - `height`: The height of the node, used for maintaining the balance of the AVL tree.
/// It is of type `i32`.
#[derive(Debug)]
pub struct Node<K: Ord, V> {
key: K,
value: V,
parent: OptNode<K, V>,
left: OptNode<K, V>,
right: OptNode<K, V>,
height: usize,
}

impl<K: Ord, V> Node<K, V> {
fn new(key: K, value: V) -> Self {
Node {
key,
value,
parent: None,
left: None,
right: None,
height: 1,
}
}

#[inline]
fn get_parent(node: OptNode<K, V>) -> OptNode<K, V> {
node.as_ref().and_then(|n| unsafe { (*n.as_ptr()).parent })
}

#[inline]
fn set_parent(child: OptNode<K, V>, parent: OptNode<K, V>) {
if parent.is_none() {
Node::set_parent(child, None);
} else {
let ordering = Node::order(parent, child);

if let Some(o) = ordering {
match o {
Ordering::Less => Node::set_right(parent, child),
Ordering::Greater => Node::set_left(parent, child),
Ordering::Equal => {
// Duplicate keys are not allowed.
// TODO(@ielm) - handle error here?
}
}
}
}
}

#[inline]
fn get_left(node: OptNode<K, V>) -> OptNode<K, V> {
node.as_ref().and_then(|n| unsafe { (*n.as_ptr()).left })
}

#[inline]
fn set_left(node: OptNode<K, V>, left: OptNode<K, V>) {
node.as_ref().map(|n| unsafe { (*n.as_ptr()).left = left });
left.as_ref()
.map(|l| unsafe { (*l.as_ptr()).parent = node });
}

#[inline]
fn get_right(node: OptNode<K, V>) -> OptNode<K, V> {
node.as_ref().and_then(|n| unsafe { (*n.as_ptr()).right })
}

#[inline]
fn set_right(node: OptNode<K, V>, right: OptNode<K, V>) {
node.as_ref()
.map(|n| unsafe { (*n.as_ptr()).right = right });
right
.as_ref()
.map(|r| unsafe { (*r.as_ptr()).parent = node });
}

#[inline]
fn order(n1: OptNode<K, V>, n2: OptNode<K, V>) -> Option<Ordering> {
let n1_key = n1.as_ref().map(|n| unsafe { &(n.as_ref()).key });
let n2_key = n2.as_ref().map(|n| unsafe { &(n.as_ref()).key });
n1_key.and_then(|n1k| n2_key.map(|n2k| n1k.cmp(n2k)))
}

#[inline]
fn unlink(parent: OptNode<K, V>, child: OptNode<K, V>) {
let ordering = Node::order(parent, child);

if let Some(o) = ordering {
match o {
Ordering::Less => {
Node::set_right(parent, None);
Node::set_parent(child, None);
}
Ordering::Greater => {
Node::set_left(parent, None);
Node::set_parent(child, None);
}
Ordering::Equal => {
// Duplicate keys are not allowed
// TODO(@ielm) - Handle error here?
}
}
}
}

#[inline]
fn get_height(node: OptNode<K, V>) -> usize {
if node.is_none() {
0
} else {
node.as_ref()
.map(|n| unsafe { (*n.as_ptr()).height })
.unwrap()
}
}

#[inline]
fn set_height(node: OptNode<K, V>, height: usize) {
node.as_ref()
.map_or_else(|| {}, |n| unsafe { (*n.as_ptr()).height = height })
}

#[inline]
fn update_height(node: OptNode<K, V>) {
let height = std::cmp::max(
Node::get_height(Node::get_left(node)),
Node::get_height(Node::get_right(node)),
) + 1;

Node::set_height(node, height);
}
}
30 changes: 30 additions & 0 deletions src/avl_tree/tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::node::{Node, OptNode};
use std::marker::PhantomData;

/// An AVL tree is a self-balancing binary search tree that maintains the
/// balance factor of each node between -1 and +1.
///
/// The `AvlTree` struct represents an AVL tree with keys of type `K` and
/// values of type `V`. The keys must implement the `Ord` trait for comparison.
///
/// The tree automatically rebalances itself after insertions and deletions to
/// ensure logarithmic time complexity for search, insertion, and deletion
/// operations.
///
/// # Type Parameters
///
/// - `K`: The type of the keys in the AVL tree. It must implement the `Ord` trait.
/// - `V`: The type of the values associated with the keys in the AVL tree.
///
/// # Fields
///
/// - `root`: The root node of the AVL tree, wrapped in an `OptNode` type alias.
/// - `len`: The number of key-value pairs stored in the AVL tree.
/// - `_marker`: A `PhantomData` marker used to express ownership and lifetime
/// relationships between the `AvlTree` and the `Node` instances.
pub struct AvlTree<K: Ord, V> {
root: OptNode<K, V>,
len: usize,
_marker: PhantomData<Box<Node<K, V>>>,
}

3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// Placeholder for ielm's tree data structures
pub mod avl_tree;

0 comments on commit 9cce056

Please sign in to comment.