From ff8421474eba5094b9d016fb596d58ef41d4aadc Mon Sep 17 00:00:00 2001 From: Brijesh03032001 Date: Sat, 25 Oct 2025 19:56:25 -0700 Subject: [PATCH 1/2] feat: add Queue data structure with BFS application - Add comprehensive Queue implementation with all operations (enqueue, dequeue, peek, etc.) - Include Priority Queue implementation as bonus feature - Add Rotten Oranges problem solution using BFS with Queue - Implement step-by-step visualization for better understanding - Update DIRECTORY.md with Queue section under Data Structures - Include detailed problem statement, examples, and complexity analysis --- DIRECTORY.md | 3 + data_structures/Queue/queue_operations.r | 293 +++++++++++++++++++ data_structures/Queue/rotten_oranges_bfs.r | 312 +++++++++++++++++++++ 3 files changed, 608 insertions(+) create mode 100644 data_structures/Queue/queue_operations.r create mode 100644 data_structures/Queue/rotten_oranges_bfs.r diff --git a/DIRECTORY.md b/DIRECTORY.md index a7a1e1b5..6e8791d8 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -41,6 +41,9 @@ ## Data Structures * [Binary Search Tree](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/binary_search_tree.r) + * Queue + * [Queue Operations](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/Queue/queue_operations.r) + * [Rotten Oranges BFS](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/Queue/rotten_oranges_bfs.r) ## Dynamic Programming * 0 diff --git a/data_structures/Queue/queue_operations.r b/data_structures/Queue/queue_operations.r new file mode 100644 index 00000000..cf0b36f3 --- /dev/null +++ b/data_structures/Queue/queue_operations.r @@ -0,0 +1,293 @@ +# Queue Data Structure Implementation in R +# +# A queue is a linear data structure that follows the First In First Out (FIFO) principle. +# Elements are added at the rear (enqueue) and removed from the front (dequeue). +# +# Time Complexities: +# - Enqueue: O(1) - adding element to rear +# - Dequeue: O(1) - removing element from front +# - Peek/Front: O(1) - viewing front element +# - Size: O(1) - getting queue size +# - IsEmpty: O(1) - checking if queue is empty +# +# Space Complexity: O(n) where n is number of elements +# +# Applications: +# - CPU scheduling and process management +# - Breadth-First Search (BFS) in graphs +# - Handling requests in web servers +# - Print queue management +# - Level order traversal in trees +# - Buffer for data streams + +# Define Queue class using Reference Classes +Queue <- setRefClass("Queue", + fields = list( + items = "list", + front_index = "numeric", + rear_index = "numeric", + max_size = "numeric" + ), + methods = list( + initialize = function(max_size = Inf) { + "Initialize an empty queue with optional maximum size" + .self$items <- list() + .self$front_index <- 1 + .self$rear_index <- 0 + .self$max_size <- max_size + cat("Queue initialized with max size:", ifelse(is.infinite(max_size), "unlimited", max_size), "\n") + }, + + enqueue = function(item) { + "Add an element to the rear of the queue" + if (.self$size() >= .self$max_size) { + stop("Queue overflow: Cannot add more elements. Max size reached: ", .self$max_size) + } + + .self$rear_index <- .self$rear_index + 1 + .self$items[[.self$rear_index]] <- item + cat("Enqueued:", item, "| Size:", .self$size(), "\n") + }, + + dequeue = function() { + "Remove and return the front element from the queue" + if (.self$is_empty()) { + stop("Queue underflow: Cannot dequeue from empty queue") + } + + item <- .self$items[[.self$front_index]] + .self$items[[.self$front_index]] <- NULL + .self$front_index <- .self$front_index + 1 + + # Reset indices when queue becomes empty to optimize memory + if (.self$is_empty()) { + .self$front_index <- 1 + .self$rear_index <- 0 + .self$items <- list() + } + + cat("Dequeued:", item, "| Size:", .self$size(), "\n") + return(item) + }, + + front = function() { + "Return the front element without removing it" + if (.self$is_empty()) { + stop("Queue is empty: No front element") + } + return(.self$items[[.self$front_index]]) + }, + + rear = function() { + "Return the rear element without removing it" + if (.self$is_empty()) { + stop("Queue is empty: No rear element") + } + return(.self$items[[.self$rear_index]]) + }, + + is_empty = function() { + "Check if the queue is empty" + return(.self$rear_index < .self$front_index) + }, + + is_full = function() { + "Check if the queue is full (only applicable for bounded queues)" + return(.self$size() >= .self$max_size) + }, + + size = function() { + "Return the number of elements in the queue" + if (.self$is_empty()) return(0) + return(.self$rear_index - .self$front_index + 1) + }, + + clear = function() { + "Remove all elements from the queue" + .self$items <- list() + .self$front_index <- 1 + .self$rear_index <- 0 + cat("Queue cleared\n") + }, + + display = function() { + "Display all elements in the queue from front to rear" + if (.self$is_empty()) { + cat("Queue is empty: []\n") + return() + } + + elements <- character(0) + for (i in .self$front_index:.self$rear_index) { + if (i <= length(.self$items) && !is.null(.self$items[[i]])) { + elements <- c(elements, as.character(.self$items[[i]])) + } + } + cat("Queue: [", paste(elements, collapse = " <- "), "] (front <- rear)\n") + }, + + to_vector = function() { + "Convert queue to vector (front to rear order)" + if (.self$is_empty()) return(c()) + + result <- c() + for (i in .self$front_index:.self$rear_index) { + result <- c(result, .self$items[[i]]) + } + return(result) + }, + + search = function(item) { + "Search for an item in the queue and return its position from front (1-indexed)" + if (.self$is_empty()) return(-1) + + for (i in .self$front_index:.self$rear_index) { + if (identical(.self$items[[i]], item)) { + return(i - .self$front_index + 1) + } + } + return(-1) # Item not found + } + ) +) + +# Utility function to demonstrate queue operations +demonstrate_queue_operations <- function() { + cat("\n=== Queue Data Structure Demonstration ===\n\n") + + # Create a queue with maximum size of 5 + q <- Queue$new(max_size = 5) + + cat("\n1. Basic Enqueue Operations:\n") + q$enqueue("A") + q$enqueue("B") + q$enqueue("C") + q$display() + + cat("\n2. Queue Status:\n") + cat("Size:", q$size(), "\n") + cat("Is Empty:", q$is_empty(), "\n") + cat("Is Full:", q$is_full(), "\n") + cat("Front element:", q$front(), "\n") + cat("Rear element:", q$rear(), "\n") + + cat("\n3. Dequeue Operations:\n") + q$dequeue() + q$display() + q$dequeue() + q$display() + + cat("\n4. More Enqueue Operations:\n") + q$enqueue("D") + q$enqueue("E") + q$enqueue("F") + q$enqueue("G") + q$display() + + cat("\n5. Search Operations:\n") + cat("Position of 'E':", q$search("E"), "\n") + cat("Position of 'Z':", q$search("Z"), "\n") + + cat("\n6. Queue to Vector:\n") + vec <- q$to_vector() + cat("As vector:", paste(vec, collapse = ", "), "\n") + + cat("\n7. Testing Queue Overflow:\n") + tryCatch({ + q$enqueue("H") # This should cause overflow + }, error = function(e) { + cat("Error caught:", e$message, "\n") + }) + + cat("\n8. Clear Queue:\n") + q$clear() + q$display() + + cat("\n9. Testing Queue Underflow:\n") + tryCatch({ + q$dequeue() # This should cause underflow + }, error = function(e) { + cat("Error caught:", e$message, "\n") + }) +} + +# Priority Queue implementation (bonus) +PriorityQueue <- setRefClass("PriorityQueue", + fields = list( + items = "list", + priorities = "numeric" + ), + methods = list( + initialize = function() { + "Initialize an empty priority queue" + .self$items <- list() + .self$priorities <- numeric(0) + cat("Priority Queue initialized\n") + }, + + enqueue = function(item, priority = 0) { + "Add an element with priority (higher number = higher priority)" + .self$items <- append(.self$items, list(item)) + .self$priorities <- append(.self$priorities, priority) + cat("Enqueued:", item, "with priority:", priority, "\n") + }, + + dequeue = function() { + "Remove and return the highest priority element" + if (length(.self$items) == 0) { + stop("Priority queue is empty") + } + + max_priority_index <- which.max(.self$priorities) + item <- .self$items[[max_priority_index]] + priority <- .self$priorities[max_priority_index] + + .self$items <- .self$items[-max_priority_index] + .self$priorities <- .self$priorities[-max_priority_index] + + cat("Dequeued:", item, "with priority:", priority, "\n") + return(list(item = item, priority = priority)) + }, + + is_empty = function() { + "Check if priority queue is empty" + return(length(.self$items) == 0) + }, + + size = function() { + "Return number of elements" + return(length(.self$items)) + }, + + display = function() { + "Display all elements with their priorities" + if (.self$is_empty()) { + cat("Priority Queue is empty\n") + return() + } + + cat("Priority Queue:\n") + for (i in seq_along(.self$items)) { + cat(" ", .self$items[[i]], "(priority:", .self$priorities[i], ")\n") + } + } + ) +) + +# Example usage and testing +if (sys.nframe() == 0) { + # Demonstrate basic queue operations + demonstrate_queue_operations() + + cat("\n\n=== Priority Queue Demonstration ===\n") + pq <- PriorityQueue$new() + pq$enqueue("Low priority task", 1) + pq$enqueue("High priority task", 5) + pq$enqueue("Medium priority task", 3) + pq$display() + + cat("\nDequeuing in priority order:\n") + while (!pq$is_empty()) { + pq$dequeue() + } +} \ No newline at end of file diff --git a/data_structures/Queue/rotten_oranges_bfs.r b/data_structures/Queue/rotten_oranges_bfs.r new file mode 100644 index 00000000..58730df1 --- /dev/null +++ b/data_structures/Queue/rotten_oranges_bfs.r @@ -0,0 +1,312 @@ +# Rotten Oranges Problem - BFS Implementation using Queue +# +# Problem Statement: +# You are given an m x n grid where each cell can have one of three values: +# - 0 representing an empty cell +# - 1 representing a fresh orange +# - 2 representing a rotten orange +# +# Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. +# Return the minimum number of minutes that must elapse until no cell has a fresh orange. +# If this is impossible, return -1. +# +# Input: grid = [[2,1,1],[1,1,0],[0,1,1]] +# Output: 4 +# Explanation: +# Minute 0: [[2,1,1],[1,1,0],[0,1,1]] +# Minute 1: [[2,2,1],[2,1,0],[0,1,1]] +# Minute 2: [[2,2,2],[2,2,0],[0,1,1]] +# Minute 3: [[2,2,2],[2,2,0],[0,2,1]] +# Minute 4: [[2,2,2],[2,2,0],[0,2,2]] +# +# Input: grid = [[2,1,1],[0,1,1],[1,0,1]] +# Output: -1 +# Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, +# because rotting only happens 4-directionally. +# +# Algorithm: Multi-source BFS +# Time Complexity: O(m * n) where m and n are grid dimensions +# Space Complexity: O(m * n) for the queue in worst case + +# Simple Queue implementation for BFS +BFSQueue <- setRefClass("BFSQueue", + fields = list( + items = "list", + front = "numeric", + rear = "numeric" + ), + methods = list( + initialize = function() { + .self$items <- list() + .self$front <- 1 + .self$rear <- 0 + }, + + enqueue = function(item) { + .self$rear <- .self$rear + 1 + .self$items[[.self$rear]] <- item + }, + + dequeue = function() { + if (.self$is_empty()) return(NULL) + item <- .self$items[[.self$front]] + .self$front <- .self$front + 1 + return(item) + }, + + is_empty = function() { + return(.self$front > .self$rear) + }, + + size = function() { + if (.self$is_empty()) return(0) + return(.self$rear - .self$front + 1) + } + ) +) + +# Main function to solve Rotten Oranges problem +rotten_oranges <- function(grid) { + if (is.null(grid) || length(grid) == 0 || length(grid[[1]]) == 0) { + return(0) + } + + rows <- length(grid) + cols <- length(grid[[1]]) + queue <- BFSQueue$new() + fresh_count <- 0 + + # Find all initially rotten oranges and count fresh ones + for (i in 1:rows) { + for (j in 1:cols) { + if (grid[[i]][[j]] == 2) { + # Add rotten orange position to queue with time 0 + queue$enqueue(list(row = i, col = j, time = 0)) + } else if (grid[[i]][[j]] == 1) { + fresh_count <- fresh_count + 1 + } + } + } + + # If no fresh oranges, return 0 + if (fresh_count == 0) return(0) + + # Directions for 4-directional movement (up, down, left, right) + directions <- list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1)) + max_time <- 0 + + # BFS to spread the rot + while (!queue$is_empty()) { + current <- queue$dequeue() + row <- current$row + col <- current$col + time <- current$time + + max_time <- max(max_time, time) + + # Check all 4 directions + for (dir in directions) { + new_row <- row + dir[1] + new_col <- col + dir[2] + + # Check bounds and if it's a fresh orange + if (new_row >= 1 && new_row <= rows && + new_col >= 1 && new_col <= cols && + grid[[new_row]][[new_col]] == 1) { + + # Make it rotten + grid[[new_row]][[new_col]] <- 2 + fresh_count <- fresh_count - 1 + + # Add to queue with incremented time + queue$enqueue(list(row = new_row, col = new_col, time = time + 1)) + } + } + } + + # If there are still fresh oranges, return -1 + if (fresh_count > 0) return(-1) + + return(max_time) +} + +# Helper function to print grid nicely +print_grid <- function(grid, title = "Grid") { + cat("\n", title, ":\n") + for (i in seq_along(grid)) { + row_str <- paste(grid[[i]], collapse = " ") + cat("[", row_str, "]\n") + } +} + +# Helper function to create grid from matrix representation +create_grid <- function(matrix_data) { + rows <- nrow(matrix_data) + cols <- ncol(matrix_data) + grid <- list() + + for (i in 1:rows) { + grid[[i]] <- list() + for (j in 1:cols) { + grid[[i]][[j]] <- matrix_data[i, j] + } + } + return(grid) +} + +# Function to demonstrate the algorithm step by step +demonstrate_rotten_oranges <- function() { + cat("=== Rotten Oranges Problem - BFS Solution ===\n") + cat("\nProblem: Given a grid with fresh oranges (1), rotten oranges (2), and empty cells (0),") + cat("\nfind minimum time for all oranges to rot. Rot spreads 4-directionally each minute.\n") + + # Test Case 1: Normal case + cat("\n--- Test Case 1 ---") + grid1_matrix <- matrix(c(2,1,1,1,1,0,0,1,1), nrow = 3, byrow = TRUE) + grid1 <- create_grid(grid1_matrix) + print_grid(grid1, "Input Grid 1") + + result1 <- rotten_oranges(grid1) + cat("Result: ", result1, " minutes\n") + cat("Explanation: All oranges can rot in", result1, "minutes\n") + + # Test Case 2: Impossible case + cat("\n--- Test Case 2 ---") + grid2_matrix <- matrix(c(2,1,1,0,1,1,1,0,1), nrow = 3, byrow = TRUE) + grid2 <- create_grid(grid2_matrix) + print_grid(grid2, "Input Grid 2") + + result2 <- rotten_oranges(grid2) + cat("Result: ", result2, "\n") + cat("Explanation: Some oranges cannot be reached, so return -1\n") + + # Test Case 3: All already rotten + cat("\n--- Test Case 3 ---") + grid3_matrix <- matrix(c(2,2,2,2,2,2), nrow = 2, byrow = TRUE) + grid3 <- create_grid(grid3_matrix) + print_grid(grid3, "Input Grid 3") + + result3 <- rotten_oranges(grid3) + cat("Result: ", result3, " minutes\n") + cat("Explanation: All oranges already rotten\n") + + # Test Case 4: No oranges + cat("\n--- Test Case 4 ---") + grid4_matrix <- matrix(c(0,0,0,0,0,0), nrow = 2, byrow = TRUE) + grid4 <- create_grid(grid4_matrix) + print_grid(grid4, "Input Grid 4") + + result4 <- rotten_oranges(grid4) + cat("Result: ", result4, " minutes\n") + cat("Explanation: No oranges to rot\n") + + # Test Case 5: Single fresh orange with no rotten ones + cat("\n--- Test Case 5 ---") + grid5_matrix <- matrix(c(1), nrow = 1, byrow = TRUE) + grid5 <- create_grid(grid5_matrix) + print_grid(grid5, "Input Grid 5") + + result5 <- rotten_oranges(grid5) + cat("Result: ", result5, "\n") + cat("Explanation: No rotten oranges to start the process\n") +} + +# Advanced version with step-by-step visualization +rotten_oranges_with_steps <- function(grid) { + if (is.null(grid) || length(grid) == 0 || length(grid[[1]]) == 0) { + return(list(result = 0, steps = list())) + } + + rows <- length(grid) + cols <- length(grid[[1]]) + queue <- BFSQueue$new() + fresh_count <- 0 + steps <- list() + + # Create a copy for visualization + working_grid <- grid + steps[[1]] <- list(time = 0, grid = working_grid, description = "Initial state") + + # Find all initially rotten oranges and count fresh ones + for (i in 1:rows) { + for (j in 1:cols) { + if (working_grid[[i]][[j]] == 2) { + queue$enqueue(list(row = i, col = j, time = 0)) + } else if (working_grid[[i]][[j]] == 1) { + fresh_count <- fresh_count + 1 + } + } + } + + if (fresh_count == 0) { + return(list(result = 0, steps = steps)) + } + + directions <- list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1)) + max_time <- 0 + current_time <- -1 + + while (!queue$is_empty()) { + current <- queue$dequeue() + row <- current$row + col <- current$col + time <- current$time + + # If we've moved to a new time step, save the grid state + if (time > current_time) { + current_time <- time + if (time > 0) { + # Deep copy the grid + step_grid <- list() + for (i in 1:rows) { + step_grid[[i]] <- list() + for (j in 1:cols) { + step_grid[[i]][[j]] <- working_grid[[i]][[j]] + } + } + steps <- append(steps, list(list( + time = time, + grid = step_grid, + description = paste("After", time, "minute(s)") + ))) + } + } + + max_time <- max(max_time, time) + + for (dir in directions) { + new_row <- row + dir[1] + new_col <- col + dir[2] + + if (new_row >= 1 && new_row <= rows && + new_col >= 1 && new_col <= cols && + working_grid[[new_row]][[new_col]] == 1) { + + working_grid[[new_row]][[new_col]] <- 2 + fresh_count <- fresh_count - 1 + queue$enqueue(list(row = new_row, col = new_col, time = time + 1)) + } + } + } + + result <- if (fresh_count > 0) -1 else max_time + return(list(result = result, steps = steps)) +} + +# Run demonstrations if script is executed directly +if (sys.nframe() == 0) { + demonstrate_rotten_oranges() + + cat("\n\n=== Step-by-Step Visualization ===\n") + grid_matrix <- matrix(c(2,1,1,1,1,0,0,1,1), nrow = 3, byrow = TRUE) + grid <- create_grid(grid_matrix) + + solution <- rotten_oranges_with_steps(grid) + + for (step in solution$steps) { + cat("\n", step$description, ":\n") + print_grid(step$grid, "") + } + + cat("\nFinal result:", solution$result, "minutes\n") +} \ No newline at end of file From de978273ddb5cf2def7e37fb9cfdaeb1e0f4189e Mon Sep 17 00:00:00 2001 From: Brijesh03032001 Date: Sun, 26 Oct 2025 01:58:53 -0700 Subject: [PATCH 2/2] Remove LeetCode problems from Queue implementation - Remove rotten_oranges_bfs.r file (LeetCode Rotten Oranges problem) - Update DIRECTORY.md to reflect pure Queue data structure only - Keep only queue_operations.r with comprehensive Queue and PriorityQueue implementations - Focus on educational data structure implementation without problem-solving examples --- DIRECTORY.md | 1 - data_structures/Queue/rotten_oranges_bfs.r | 312 --------------------- 2 files changed, 313 deletions(-) delete mode 100644 data_structures/Queue/rotten_oranges_bfs.r diff --git a/DIRECTORY.md b/DIRECTORY.md index 6e8791d8..f1f26465 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -43,7 +43,6 @@ * [Binary Search Tree](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/binary_search_tree.r) * Queue * [Queue Operations](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/Queue/queue_operations.r) - * [Rotten Oranges BFS](https://github.com/TheAlgorithms/R/blob/HEAD/data_structures/Queue/rotten_oranges_bfs.r) ## Dynamic Programming * 0 diff --git a/data_structures/Queue/rotten_oranges_bfs.r b/data_structures/Queue/rotten_oranges_bfs.r deleted file mode 100644 index 58730df1..00000000 --- a/data_structures/Queue/rotten_oranges_bfs.r +++ /dev/null @@ -1,312 +0,0 @@ -# Rotten Oranges Problem - BFS Implementation using Queue -# -# Problem Statement: -# You are given an m x n grid where each cell can have one of three values: -# - 0 representing an empty cell -# - 1 representing a fresh orange -# - 2 representing a rotten orange -# -# Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten. -# Return the minimum number of minutes that must elapse until no cell has a fresh orange. -# If this is impossible, return -1. -# -# Input: grid = [[2,1,1],[1,1,0],[0,1,1]] -# Output: 4 -# Explanation: -# Minute 0: [[2,1,1],[1,1,0],[0,1,1]] -# Minute 1: [[2,2,1],[2,1,0],[0,1,1]] -# Minute 2: [[2,2,2],[2,2,0],[0,1,1]] -# Minute 3: [[2,2,2],[2,2,0],[0,2,1]] -# Minute 4: [[2,2,2],[2,2,0],[0,2,2]] -# -# Input: grid = [[2,1,1],[0,1,1],[1,0,1]] -# Output: -1 -# Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, -# because rotting only happens 4-directionally. -# -# Algorithm: Multi-source BFS -# Time Complexity: O(m * n) where m and n are grid dimensions -# Space Complexity: O(m * n) for the queue in worst case - -# Simple Queue implementation for BFS -BFSQueue <- setRefClass("BFSQueue", - fields = list( - items = "list", - front = "numeric", - rear = "numeric" - ), - methods = list( - initialize = function() { - .self$items <- list() - .self$front <- 1 - .self$rear <- 0 - }, - - enqueue = function(item) { - .self$rear <- .self$rear + 1 - .self$items[[.self$rear]] <- item - }, - - dequeue = function() { - if (.self$is_empty()) return(NULL) - item <- .self$items[[.self$front]] - .self$front <- .self$front + 1 - return(item) - }, - - is_empty = function() { - return(.self$front > .self$rear) - }, - - size = function() { - if (.self$is_empty()) return(0) - return(.self$rear - .self$front + 1) - } - ) -) - -# Main function to solve Rotten Oranges problem -rotten_oranges <- function(grid) { - if (is.null(grid) || length(grid) == 0 || length(grid[[1]]) == 0) { - return(0) - } - - rows <- length(grid) - cols <- length(grid[[1]]) - queue <- BFSQueue$new() - fresh_count <- 0 - - # Find all initially rotten oranges and count fresh ones - for (i in 1:rows) { - for (j in 1:cols) { - if (grid[[i]][[j]] == 2) { - # Add rotten orange position to queue with time 0 - queue$enqueue(list(row = i, col = j, time = 0)) - } else if (grid[[i]][[j]] == 1) { - fresh_count <- fresh_count + 1 - } - } - } - - # If no fresh oranges, return 0 - if (fresh_count == 0) return(0) - - # Directions for 4-directional movement (up, down, left, right) - directions <- list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1)) - max_time <- 0 - - # BFS to spread the rot - while (!queue$is_empty()) { - current <- queue$dequeue() - row <- current$row - col <- current$col - time <- current$time - - max_time <- max(max_time, time) - - # Check all 4 directions - for (dir in directions) { - new_row <- row + dir[1] - new_col <- col + dir[2] - - # Check bounds and if it's a fresh orange - if (new_row >= 1 && new_row <= rows && - new_col >= 1 && new_col <= cols && - grid[[new_row]][[new_col]] == 1) { - - # Make it rotten - grid[[new_row]][[new_col]] <- 2 - fresh_count <- fresh_count - 1 - - # Add to queue with incremented time - queue$enqueue(list(row = new_row, col = new_col, time = time + 1)) - } - } - } - - # If there are still fresh oranges, return -1 - if (fresh_count > 0) return(-1) - - return(max_time) -} - -# Helper function to print grid nicely -print_grid <- function(grid, title = "Grid") { - cat("\n", title, ":\n") - for (i in seq_along(grid)) { - row_str <- paste(grid[[i]], collapse = " ") - cat("[", row_str, "]\n") - } -} - -# Helper function to create grid from matrix representation -create_grid <- function(matrix_data) { - rows <- nrow(matrix_data) - cols <- ncol(matrix_data) - grid <- list() - - for (i in 1:rows) { - grid[[i]] <- list() - for (j in 1:cols) { - grid[[i]][[j]] <- matrix_data[i, j] - } - } - return(grid) -} - -# Function to demonstrate the algorithm step by step -demonstrate_rotten_oranges <- function() { - cat("=== Rotten Oranges Problem - BFS Solution ===\n") - cat("\nProblem: Given a grid with fresh oranges (1), rotten oranges (2), and empty cells (0),") - cat("\nfind minimum time for all oranges to rot. Rot spreads 4-directionally each minute.\n") - - # Test Case 1: Normal case - cat("\n--- Test Case 1 ---") - grid1_matrix <- matrix(c(2,1,1,1,1,0,0,1,1), nrow = 3, byrow = TRUE) - grid1 <- create_grid(grid1_matrix) - print_grid(grid1, "Input Grid 1") - - result1 <- rotten_oranges(grid1) - cat("Result: ", result1, " minutes\n") - cat("Explanation: All oranges can rot in", result1, "minutes\n") - - # Test Case 2: Impossible case - cat("\n--- Test Case 2 ---") - grid2_matrix <- matrix(c(2,1,1,0,1,1,1,0,1), nrow = 3, byrow = TRUE) - grid2 <- create_grid(grid2_matrix) - print_grid(grid2, "Input Grid 2") - - result2 <- rotten_oranges(grid2) - cat("Result: ", result2, "\n") - cat("Explanation: Some oranges cannot be reached, so return -1\n") - - # Test Case 3: All already rotten - cat("\n--- Test Case 3 ---") - grid3_matrix <- matrix(c(2,2,2,2,2,2), nrow = 2, byrow = TRUE) - grid3 <- create_grid(grid3_matrix) - print_grid(grid3, "Input Grid 3") - - result3 <- rotten_oranges(grid3) - cat("Result: ", result3, " minutes\n") - cat("Explanation: All oranges already rotten\n") - - # Test Case 4: No oranges - cat("\n--- Test Case 4 ---") - grid4_matrix <- matrix(c(0,0,0,0,0,0), nrow = 2, byrow = TRUE) - grid4 <- create_grid(grid4_matrix) - print_grid(grid4, "Input Grid 4") - - result4 <- rotten_oranges(grid4) - cat("Result: ", result4, " minutes\n") - cat("Explanation: No oranges to rot\n") - - # Test Case 5: Single fresh orange with no rotten ones - cat("\n--- Test Case 5 ---") - grid5_matrix <- matrix(c(1), nrow = 1, byrow = TRUE) - grid5 <- create_grid(grid5_matrix) - print_grid(grid5, "Input Grid 5") - - result5 <- rotten_oranges(grid5) - cat("Result: ", result5, "\n") - cat("Explanation: No rotten oranges to start the process\n") -} - -# Advanced version with step-by-step visualization -rotten_oranges_with_steps <- function(grid) { - if (is.null(grid) || length(grid) == 0 || length(grid[[1]]) == 0) { - return(list(result = 0, steps = list())) - } - - rows <- length(grid) - cols <- length(grid[[1]]) - queue <- BFSQueue$new() - fresh_count <- 0 - steps <- list() - - # Create a copy for visualization - working_grid <- grid - steps[[1]] <- list(time = 0, grid = working_grid, description = "Initial state") - - # Find all initially rotten oranges and count fresh ones - for (i in 1:rows) { - for (j in 1:cols) { - if (working_grid[[i]][[j]] == 2) { - queue$enqueue(list(row = i, col = j, time = 0)) - } else if (working_grid[[i]][[j]] == 1) { - fresh_count <- fresh_count + 1 - } - } - } - - if (fresh_count == 0) { - return(list(result = 0, steps = steps)) - } - - directions <- list(c(-1, 0), c(1, 0), c(0, -1), c(0, 1)) - max_time <- 0 - current_time <- -1 - - while (!queue$is_empty()) { - current <- queue$dequeue() - row <- current$row - col <- current$col - time <- current$time - - # If we've moved to a new time step, save the grid state - if (time > current_time) { - current_time <- time - if (time > 0) { - # Deep copy the grid - step_grid <- list() - for (i in 1:rows) { - step_grid[[i]] <- list() - for (j in 1:cols) { - step_grid[[i]][[j]] <- working_grid[[i]][[j]] - } - } - steps <- append(steps, list(list( - time = time, - grid = step_grid, - description = paste("After", time, "minute(s)") - ))) - } - } - - max_time <- max(max_time, time) - - for (dir in directions) { - new_row <- row + dir[1] - new_col <- col + dir[2] - - if (new_row >= 1 && new_row <= rows && - new_col >= 1 && new_col <= cols && - working_grid[[new_row]][[new_col]] == 1) { - - working_grid[[new_row]][[new_col]] <- 2 - fresh_count <- fresh_count - 1 - queue$enqueue(list(row = new_row, col = new_col, time = time + 1)) - } - } - } - - result <- if (fresh_count > 0) -1 else max_time - return(list(result = result, steps = steps)) -} - -# Run demonstrations if script is executed directly -if (sys.nframe() == 0) { - demonstrate_rotten_oranges() - - cat("\n\n=== Step-by-Step Visualization ===\n") - grid_matrix <- matrix(c(2,1,1,1,1,0,0,1,1), nrow = 3, byrow = TRUE) - grid <- create_grid(grid_matrix) - - solution <- rotten_oranges_with_steps(grid) - - for (step in solution$steps) { - cat("\n", step$description, ":\n") - print_grid(step$grid, "") - } - - cat("\nFinal result:", solution$result, "minutes\n") -} \ No newline at end of file