Stack Operations
S
# Stack Data Structure Implementation in R
#
# A stack is a linear data structure that follows the Last In First Out (LIFO) principle.
# Elements are added and removed from the same end, called the top of the stack.
#
# Time Complexities:
# - Push: O(1) - adding element to top
# - Pop: O(1) - removing element from top
# - Peek/Top: O(1) - viewing top element
# - Size: O(1) - getting stack size
# - IsEmpty: O(1) - checking if stack is empty
#
# Space Complexity: O(n) where n is number of elements
#
# Applications:
# - Function call management (call stack)
# - Expression evaluation and syntax parsing
# - Undo operations in text editors
# - Browser history (back button)
# - Depth-First Search (DFS) in graphs
# - Backtracking algorithms
# - Memory management in programming languages
# Define Stack class using Reference Classes
Stack <- setRefClass("Stack",
fields = list(
items = "list",
top_index = "numeric",
max_size = "numeric"
),
methods = list(
initialize = function(max_size = Inf) {
"Initialize an empty stack with optional maximum size"
.self$items <- list()
.self$top_index <- 0
.self$max_size <- max_size
cat("Stack initialized with max size:", ifelse(is.infinite(max_size), "unlimited", max_size), "\n")
},
push = function(item) {
"Add an element to the top of the stack"
if (.self$size() >= .self$max_size) {
stop("Stack overflow: Cannot push more elements. Max size reached: ", .self$max_size)
}
.self$top_index <- .self$top_index + 1
.self$items[[.self$top_index]] <- item
cat("Pushed:", item, "| Size:", .self$size(), "\n")
},
pop = function() {
"Remove and return the top element from the stack"
if (.self$is_empty()) {
stop("Stack underflow: Cannot pop from empty stack")
}
item <- .self$items[[.self$top_index]]
.self$items[[.self$top_index]] <- NULL
.self$top_index <- .self$top_index - 1
# Clean up the list to optimize memory
if (.self$top_index < length(.self$items)) {
.self$items <- .self$items[1:.self$top_index]
}
cat("Popped:", item, "| Size:", .self$size(), "\n")
return(item)
},
peek = function() {
"Return the top element without removing it"
if (.self$is_empty()) {
stop("Stack is empty: No top element")
}
return(.self$items[[.self$top_index]])
},
top = function() {
"Alias for peek() - return the top element without removing it"
return(.self$peek())
},
is_empty = function() {
"Check if the stack is empty"
return(.self$top_index == 0)
},
is_full = function() {
"Check if the stack is full (only applicable for bounded stacks)"
return(.self$size() >= .self$max_size)
},
size = function() {
"Return the number of elements in the stack"
return(.self$top_index)
},
clear = function() {
"Remove all elements from the stack"
.self$items <- list()
.self$top_index <- 0
cat("Stack cleared\n")
},
display = function() {
"Display all elements in the stack from bottom to top"
if (.self$is_empty()) {
cat("Stack is empty: []\n")
return()
}
elements <- character(0)
for (i in 1:.self$top_index) {
elements <- c(elements, as.character(.self$items[[i]]))
}
cat("Stack: [", paste(elements, collapse = " | "), "] (bottom → top)\n")
},
to_vector = function() {
"Convert stack to vector (bottom to top order)"
if (.self$is_empty()) return(c())
result <- c()
for (i in 1:.self$top_index) {
result <- c(result, .self$items[[i]])
}
return(result)
},
search = function(item) {
"Search for an item in the stack and return its position from top (1-indexed)"
if (.self$is_empty()) return(-1)
for (i in .self$top_index:1) {
if (identical(.self$items[[i]], item)) {
return(.self$top_index - i + 1) # Position from top
}
}
return(-1) # Item not found
},
reverse = function() {
"Reverse the order of elements in the stack"
if (.self$size() <= 1) return()
# Create a temporary vector and reverse it
temp_items <- .self$to_vector()
temp_items <- rev(temp_items)
# Rebuild stack with reversed order
.self$clear()
for (item in temp_items) {
.self$push(item)
}
cat("Stack reversed\n")
}
)
)
# Utility function to demonstrate stack operations
demonstrate_stack_operations <- function() {
cat("\n=== Stack Data Structure Demonstration ===\n\n")
# Create a stack with maximum size of 5
s <- Stack$new(max_size = 5)
cat("\n1. Basic Push Operations:\n")
s$push("A")
s$push("B")
s$push("C")
s$display()
cat("\n2. Stack Status:\n")
cat("Size:", s$size(), "\n")
cat("Is Empty:", s$is_empty(), "\n")
cat("Is Full:", s$is_full(), "\n")
cat("Top element:", s$peek(), "\n")
cat("\n3. Pop Operations:\n")
s$pop()
s$display()
s$pop()
s$display()
cat("\n4. More Push Operations:\n")
s$push("D")
s$push("E")
s$push("F")
s$push("G")
s$display()
cat("\n5. Search Operations:\n")
cat("Position of 'E' from top:", s$search("E"), "\n")
cat("Position of 'Z' from top:", s$search("Z"), "\n")
cat("\n6. Stack to Vector:\n")
vec <- s$to_vector()
cat("As vector:", paste(vec, collapse = ", "), "\n")
cat("\n7. Reverse Stack:\n")
s$reverse()
s$display()
cat("\n8. Testing Stack Overflow:\n")
tryCatch({
s$push("H") # This should cause overflow
}, error = function(e) {
cat("Error caught:", e$message, "\n")
})
cat("\n9. Clear Stack:\n")
s$clear()
s$display()
cat("\n10. Testing Stack Underflow:\n")
tryCatch({
s$pop() # This should cause underflow
}, error = function(e) {
cat("Error caught:", e$message, "\n")
})
}
# Expression Evaluator using Stack (bonus feature)
ExpressionEvaluator <- setRefClass("ExpressionEvaluator",
fields = list(
operators = "character",
precedence = "list"
),
methods = list(
initialize = function() {
"Initialize expression evaluator with operator precedence"
.self$operators <- c("+", "-", "*", "/", "^", "(", ")")
.self$precedence <- list("+" = 1, "-" = 1, "*" = 2, "/" = 2, "^" = 3, "(" = 0, ")" = 0)
cat("Expression Evaluator initialized\n")
},
is_operator = function(char) {
"Check if character is an operator"
return(char %in% .self$operators)
},
get_precedence = function(op) {
"Get precedence of an operator"
return(.self$precedence[[op]])
},
infix_to_postfix = function(expression) {
"Convert infix expression to postfix notation"
# Remove spaces and split into tokens
tokens <- strsplit(gsub(" ", "", expression), "")[[1]]
result <- character(0)
stack <- Stack$new()
for (token in tokens) {
if (!.self$is_operator(token)) {
# Operand
result <- c(result, token)
} else if (token == "(") {
stack$push(token)
} else if (token == ")") {
while (!stack$is_empty() && stack$peek() != "(") {
result <- c(result, stack$pop())
}
if (!stack$is_empty()) stack$pop() # Remove the '('
} else {
# Operator
while (!stack$is_empty() &&
.self$get_precedence(stack$peek()) >= .self$get_precedence(token)) {
result <- c(result, stack$pop())
}
stack$push(token)
}
}
# Pop remaining operators
while (!stack$is_empty()) {
result <- c(result, stack$pop())
}
return(paste(result, collapse = " "))
},
evaluate_postfix = function(expression) {
"Evaluate postfix expression"
tokens <- strsplit(expression, " ")[[1]]
stack <- Stack$new()
for (token in tokens) {
if (!.self$is_operator(token)) {
# Operand
stack$push(as.numeric(token))
} else {
# Operator
if (stack$size() < 2) {
stop("Invalid expression: insufficient operands")
}
b <- stack$pop()
a <- stack$pop()
result <- switch(token,
"+" = a + b,
"-" = a - b,
"*" = a * b,
"/" = if(b != 0) a / b else stop("Division by zero"),
"^" = a ^ b,
stop("Unknown operator:", token)
)
stack$push(result)
}
}
if (stack$size() != 1) {
stop("Invalid expression: too many operands")
}
return(stack$pop())
},
evaluate_infix = function(expression) {
"Evaluate infix expression by converting to postfix first"
postfix <- .self$infix_to_postfix(expression)
cat("Infix:", expression, "\n")
cat("Postfix:", postfix, "\n")
result <- .self$evaluate_postfix(postfix)
cat("Result:", result, "\n")
return(result)
}
)
)
# Balanced Parentheses Checker
check_balanced_parentheses <- function(expression) {
"Check if parentheses are balanced in an expression"
stack <- Stack$new()
opening <- c("(", "[", "{")
closing <- c(")", "]", "}")
pairs <- list(")" = "(", "]" = "[", "}" = "{")
chars <- strsplit(expression, "")[[1]]
for (char in chars) {
if (char %in% opening) {
stack$push(char)
} else if (char %in% closing) {
if (stack$is_empty()) {
return(FALSE) # Closing bracket without opening
}
if (stack$pop() != pairs[[char]]) {
return(FALSE) # Mismatched brackets
}
}
}
return(stack$is_empty()) # Should be empty if balanced
}
# Function Call Stack Simulator
FunctionCallStack <- setRefClass("FunctionCallStack",
fields = list(
call_stack = "ANY"
),
methods = list(
initialize = function() {
.self$call_stack <- Stack$new()
cat("Function Call Stack initialized\n")
},
call_function = function(func_name, params = "") {
"Simulate a function call"
call_info <- paste0(func_name, "(", params, ")")
.self$call_stack$push(call_info)
cat("Called:", call_info, "| Stack depth:", .self$call_stack$size(), "\n")
},
return_from_function = function() {
"Simulate returning from a function"
if (.self$call_stack$is_empty()) {
cat("No function to return from\n")
return()
}
returned_func <- .self$call_stack$pop()
cat("Returned from:", returned_func, "| Stack depth:", .self$call_stack$size(), "\n")
},
show_call_stack = function() {
"Display the current call stack"
if (.self$call_stack$is_empty()) {
cat("Call stack is empty\n")
return()
}
cat("Current call stack (bottom to top):\n")
.self$call_stack$display()
}
)
)
# Example usage and testing
if (sys.nframe() == 0) {
# Demonstrate basic stack operations
demonstrate_stack_operations()
cat("\n\n=== Expression Evaluation Demonstration ===\n")
evaluator <- ExpressionEvaluator$new()
# Test infix to postfix conversion and evaluation
cat("\nExpression 1:\n")
evaluator$evaluate_infix("2 + 3 * 4")
cat("\nExpression 2:\n")
evaluator$evaluate_infix("(2 + 3) * 4")
cat("\nExpression 3:\n")
evaluator$evaluate_infix("2 ^ 3 + 1")
cat("\n\n=== Balanced Parentheses Check ===\n")
test_expressions <- c(
"(())",
"((()))",
"()[]{}",
"([{}])",
"(()",
"())",
"([)]"
)
for (expr in test_expressions) {
result <- check_balanced_parentheses(expr)
cat("'", expr, "' is", ifelse(result, "balanced", "not balanced"), "\n")
}
cat("\n\n=== Function Call Stack Simulation ===\n")
call_stack <- FunctionCallStack$new()
call_stack$call_function("main", "")
call_stack$call_function("foo", "x, y")
call_stack$call_function("bar", "z")
call_stack$show_call_stack()
call_stack$return_from_function()
call_stack$return_from_function()
call_stack$show_call_stack()
call_stack$return_from_function()
call_stack$show_call_stack()
}