module datatypes /// Internal rapresentation of the tree node [heap] struct BSTreeNode[T] { mut: // Mark a node as initialized is_init bool // Value of the node value T // The parent of the node parent &BSTreeNode[T] = unsafe { 0 } // The left side with value less than the // value of this node left &BSTreeNode[T] = unsafe { 0 } // The right side with value grater than the // value of thiss node right &BSTreeNode[T] = unsafe { 0 } } // Create new root bst node fn new_root_node[T](value T) &BSTreeNode[T] { return &BSTreeNode[T]{ is_init: true value: value parent: new_none_node[T](true) left: new_none_node[T](false) right: new_none_node[T](false) } } // new_node creates a new bst node with a parent reference. fn new_node[T](parent &BSTreeNode[T], value T) &BSTreeNode[T] { return &BSTreeNode[T]{ is_init: true value: value parent: parent } } // new_none_node creates a dummy node. fn new_none_node[T](init bool) &BSTreeNode[T] { return &BSTreeNode[T]{ is_init: init } } // bind to an actual instance of a node. fn (mut node BSTreeNode[T]) bind(mut to_bind BSTreeNode[T], left bool) { node.left = to_bind.left node.right = to_bind.right node.value = to_bind.value node.is_init = to_bind.is_init to_bind = new_none_node[T](false) } // Pure Binary Seach Tree implementation // // Pure V implementation of the Binary Search Tree // Time complexity of main operation O(log N) // Space complexity O(N) pub struct BSTree[T] { mut: root &BSTreeNode[T] = unsafe { 0 } } // insert give the possibility to insert an element in the BST. pub fn (mut bst BSTree[T]) insert(value T) bool { if bst.is_empty() { bst.root = new_root_node(value) return true } return bst.insert_helper(mut bst.root, value) } // insert_helper walks the tree and inserts the given node. fn (mut bst BSTree[T]) insert_helper(mut node BSTreeNode[T], value T) bool { if node.value < value { if unsafe { node.right != 0 } && node.right.is_init { return bst.insert_helper(mut node.right, value) } node.right = new_node(node, value) return true } else if node.value > value { if unsafe { node.left != 0 } && node.left.is_init { return bst.insert_helper(mut node.left, value) } node.left = new_node(node, value) return true } return false } // contains checks if an element with a given `value` is inside the BST. pub fn (bst &BSTree[T]) contains(value T) bool { return bst.contains_helper(bst.root, value) } // contains_helper is a helper function to walk the tree, and return // the absence or presence of the `value`. fn (bst &BSTree[T]) contains_helper(node &BSTreeNode[T], value T) bool { if unsafe { node == 0 } || !node.is_init { return false } if node.value < value { return bst.contains_helper(node.right, value) } else if node.value > value { return bst.contains_helper(node.left, value) } assert node.value == value return true } // remove removes an element with `value` from the BST. pub fn (mut bst BSTree[T]) remove(value T) bool { if bst.is_empty() { return false } return bst.remove_helper(mut bst.root, value, false) } fn (mut bst BSTree[T]) remove_helper(mut node BSTreeNode[T], value T, left bool) bool { if !node.is_init { return false } if node.value == value { if unsafe { node.left != 0 } && node.left.is_init { // In order to remove the element we need to bring up as parent the max of the // left sub-tree. mut max_node := bst.get_max_from_right(node.left) node.bind(mut max_node, true) } else if unsafe { node.right != 0 } && node.right.is_init { // Bring up the element with the minimum value in the right sub-tree. mut min_node := bst.get_min_from_left(node.right) node.bind(mut min_node, false) } else { mut parent := node.parent if left { parent.left = new_none_node[T](false) } else { parent.right = new_none_node[T](false) } node = new_none_node[T](false) } return true } if node.value < value { return bst.remove_helper(mut node.right, value, false) } return bst.remove_helper(mut node.left, value, true) } // get_max_from_right returns the max element of the BST following the right branch. fn (bst &BSTree[T]) get_max_from_right(node &BSTreeNode[T]) &BSTreeNode[T] { if unsafe { node == 0 } { return new_none_node[T](false) } right_node := node.right if unsafe { right_node == 0 } || !right_node.is_init { return node } return bst.get_max_from_right(right_node) } // get_min_from_left returns the min element of the BST by following the left branch. fn (bst &BSTree[T]) get_min_from_left(node &BSTreeNode[T]) &BSTreeNode[T] { if unsafe { node == 0 } { return new_none_node[T](false) } left_node := node.left if unsafe { left_node == 0 } || !left_node.is_init { return node } return bst.get_min_from_left(left_node) } // is_empty checks if the BST is empty pub fn (bst &BSTree[T]) is_empty() bool { return unsafe { bst.root == 0 } } // in_order_traversal traverses the BST in order, and returns the result as an array. pub fn (bst &BSTree[T]) in_order_traversal() []T { mut result := []T{} bst.in_order_traversal_helper(bst.root, mut result) return result } // in_order_traversal_helper helps traverse the BST, and accumulates the result in the `result` array. fn (bst &BSTree[T]) in_order_traversal_helper(node &BSTreeNode[T], mut result []T) { if unsafe { node == 0 } || !node.is_init { return } bst.in_order_traversal_helper(node.left, mut result) result << node.value bst.in_order_traversal_helper(node.right, mut result) } // post_order_traversal traverses the BST in post order, and returns the result in an array. pub fn (bst &BSTree[T]) post_order_traversal() []T { mut result := []T{} bst.post_order_traversal_helper(bst.root, mut result) return result } // post_order_traversal_helper is a helper function that traverses the BST in post order, // accumulating the result in an array. fn (bst &BSTree[T]) post_order_traversal_helper(node &BSTreeNode[T], mut result []T) { if unsafe { node == 0 } || !node.is_init { return } bst.post_order_traversal_helper(node.left, mut result) bst.post_order_traversal_helper(node.right, mut result) result << node.value } // pre_order_traversal traverses the BST in pre order, and returns the result as an array. pub fn (bst &BSTree[T]) pre_order_traversal() []T { mut result := []T{} bst.pre_order_traversal_helper(bst.root, mut result) return result } // pre_order_traversal_helper is a helper function to traverse the BST // in pre order and accumulates the results in an array. fn (bst &BSTree[T]) pre_order_traversal_helper(node &BSTreeNode[T], mut result []T) { if unsafe { node == 0 } || !node.is_init { return } result << node.value bst.pre_order_traversal_helper(node.left, mut result) bst.pre_order_traversal_helper(node.right, mut result) } // get_node is a helper method to ge the internal rapresentation of the node with the `value`. fn (bst &BSTree[T]) get_node(node &BSTreeNode[T], value T) &BSTreeNode[T] { if unsafe { node == 0 } || !node.is_init { return new_none_node[T](false) } if node.value == value { return node } if node.value < value { return bst.get_node(node.right, value) } return bst.get_node(node.left, value) } // to_left returns the value of the node to the left of the node with `value` specified if it exists, // otherwise the a false value is returned. // // An example of usage can be the following one //```v // left_value, exist := bst.to_left(10) //``` pub fn (bst &BSTree[T]) to_left(value T) !T { if bst.is_empty() { return error('BSTree is empty') } node := bst.get_node(bst.root, value) if !node.is_init { return error('BSTree is not initialized') } left_node := node.left return left_node.value } // to_right return the value of the element to the right of the node with `value` specified, if exist // otherwise, the boolean value is false // An example of usage can be the following one // //```v // left_value, exist := bst.to_right(10) //``` pub fn (bst &BSTree[T]) to_right(value T) !T { if bst.is_empty() { return error('BSTree is empty') } node := bst.get_node(bst.root, value) if !node.is_init { return error('BSTree is not initialized') } right_node := node.right return right_node.value } // max return the max element inside the BST. // Time complexity O(N) if the BST is not balanced pub fn (bst &BSTree[T]) max() !T { if bst.is_empty() { return error('BSTree is empty') } max := bst.get_max_from_right(bst.root) if !max.is_init { return error('BSTree is not initialized') } return max.value } // min return the minimum element in the BST. // Time complexity O(N) if the BST is not balanced. pub fn (bst &BSTree[T]) min() !T { if bst.is_empty() { return error('BSTree is empty') } min := bst.get_min_from_left(bst.root) if !min.is_init { return error('BSTree is not initialized') } return min.value }