If it is just a programming exercise and you wish to use pure R, the only other way is by using environments. No other R objects are passed by "reference", i.e. changes made to args are "visible" after a function call.
Here is a very primitive implementation of a BST on environments, that mimics the usage of dynamically allocated memory and pointers. I do not recommend using it in practice. This solution is just for fun.
If you need an access to a "ordered set" container, you should rather use STL's set
in an RCpp program.
A "School" implementation of a BST
# assumption: elements can be compared with < and ==
# Each node will be represented by a list with 3 elements
# (object, left, right)
# instead of pointer we will use strings
# note that the maximal number of nodes
# that can be created is restricted
# create a new empty tree
bst_new <- function() {
e <- new.env()
e$root <- NULL
e$last <- 0L # this will emulate a "heap"
class(e) <- 'bst'
e
}
# insert an element
# duplicates are ignored
bst_insert <- function(bst, val) {
stopifnot(is.environment(bst), class(bst) == 'bst')
if (is.null(bst$root)) {
bst$root <- as.character(bst$last)
bst$last <- bst$last + 1L
assign(bst$root, list(val, NULL, NULL), bst)
}
else {
cur_id <- bst$root
repeat {
cur_node <- get(cur_id, bst)
if (val == cur_node[[1]])
return(invisible(NULL)) # ignore
else if (val < cur_node[[1]]) {
if (is.null(cur_node[[2]])) {
cur_node[[2]] <- as.character(bst$last)
assign(cur_id, cur_node, bst)
bst$last <- bst$last + 1L
assign(cur_node[[2]], list(val, NULL, NULL), bst)
return(invisible(NULL))
}
else {
cur_id <- cur_node[[2]]
}
}
else {
if (is.null(cur_node[[3]])) {
cur_node[[3]] <- as.character(bst$last)
assign(cur_id, cur_node, bst)
bst$last <- bst$last + 1L
assign(cur_node[[3]], list(val, NULL, NULL), bst)
return(invisible(NULL))
}
else {
cur_id <- cur_node[[3]]
}
}
}
}
}
# print all elems, in order
bst_print <- function(bst) # or print.bst
{
stopifnot(is.environment(bst), class(bst) == 'bst')
bst_print_tmp <- function(bst, id_node) {
if (is.null(id_node)) return(invisible(NULL))
cur_node <- get(as.character(id_node), envir=bst)
bst_print_tmp(bst, cur_node[[2]]) # left
print(cur_node[[1]]) # this
bst_print_tmp(bst, cur_node[[3]]) # right
}
bst_print_tmp(bst, bst$root)
invisible(NULL)
}
tree <- bst_new()
bst_insert(tree, 3)
bst_insert(tree, 5)
bst_insert(tree, 1)
bst_insert(tree, 2)
bst_insert(tree, 8)
bst_insert(tree, 7)
bst_print(tree)
## [1] 1
## [1] 2
## [1] 3
## [1] 5
## [1] 7
## [1] 8