diff --git a/DIRECTORY.md b/DIRECTORY.md index 0a0560e5..9285bf66 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -58,6 +58,12 @@ * [Dijkstra Shortest Path](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/dijkstra_shortest_path.r) * [Floyd Warshall](https://github.com/TheAlgorithms/R/blob/HEAD/graph_algorithms/floyd_warshall.r) + +## Linked List Algorithms + * [Circular Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/circular_linked_list.r) + * [Doubly Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/doubly_linked_list.r) + * [Singly Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/singly_linked_list.r) + ## Machine Learning * [Gradient Boosting](https://github.com/TheAlgorithms/R/blob/HEAD/machine_learning/gradient_boosting.r) diff --git a/linked_list_algorithms/README.md b/linked_list_algorithms/README.md new file mode 100644 index 00000000..017e034a --- /dev/null +++ b/linked_list_algorithms/README.md @@ -0,0 +1,293 @@ +# Linked List Algorithms + +This directory contains comprehensive implementations of various linked list data structures in R. Linked lists are fundamental linear data structures where elements are stored in nodes, and each node contains data and reference(s) to other nodes. + +## 📋 Contents + +- [`singly_linked_list.r`](singly_linked_list.r) - Singly Linked List implementation +- [`doubly_linked_list.r`](doubly_linked_list.r) - Doubly Linked List implementation +- [`circular_linked_list.r`](circular_linked_list.r) - Circular Linked List implementation +- [`README.md`](README.md) - This documentation file + +## 🔗 Linked List Types Overview + +### 1. Singly Linked List +**Structure**: Each node contains data and a pointer to the next node +**Traversal**: Forward only (head → tail) +**Memory**: Minimal overhead (one pointer per node) + +**Use Cases**: +- Implementing stacks and queues +- Undo functionality in applications +- Simple sequential data storage + +### 2. Doubly Linked List +**Structure**: Each node contains data and pointers to both next and previous nodes +**Traversal**: Bidirectional (forward and backward) +**Memory**: Higher overhead (two pointers per node) + +**Use Cases**: +- Browser history navigation (back/forward) +- LRU cache implementation +- Complex navigation systems + +### 3. Circular Linked List +**Structure**: Last node points back to the first node (forms a circle) +**Traversal**: Infinite circular traversal possible +**Memory**: Same as singly linked (one pointer per node) + +**Use Cases**: +- Round-robin CPU scheduling +- Multiplayer turn-based games +- Continuous playlists + +## ⚡ Performance Comparison + +| Operation | Singly | Doubly | Circular | +|-----------|--------|--------|----------| +| Insert at Head | O(1) | O(1) | O(1) | +| Insert at Tail | O(n) | O(1) | O(n) | +| Delete from Head | O(1) | O(1) | O(1) | +| Delete from Tail | O(n) | O(1) | O(n) | +| Search | O(n) | O(n) | O(n) | +| Access by Index | O(n) | O(n/2)* | O(n) | + +*Doubly linked list can traverse from closer end (head or tail) + +## 🚀 Quick Start Guide + +### Installation +No additional packages required! Just R base installation with `setRefClass` support. + +### Basic Usage Examples + +#### Singly Linked List +```r +source("singly_linked_list.r") + +# Create new list +my_list <- SinglyLinkedList$new() + +# Basic operations +my_list$insert_at_head(10) +my_list$insert_at_tail(20) +my_list$insert_at_position(15, 1) +my_list$print_list() + +# Access and search +value <- my_list$get(1) # Get element at position 1 +position <- my_list$search(15) # Find position of value 15 +vector_form <- my_list$to_vector() # Convert to R vector + +# Delete operations +my_list$delete_from_head() +my_list$delete_by_value(15) +``` + +#### Doubly Linked List +```r +source("doubly_linked_list.r") + +# Create new doubly linked list +dll <- DoublyLinkedList$new() + +# Bidirectional operations +dll$insert_at_head(10) +dll$insert_at_tail(20) +dll$print_list() # Forward traversal +dll$print_list_reverse() # Backward traversal + +# Optimized access (traverses from closer end) +dll$get(0) # Accessed from head +dll$get(dll$get_size() - 1) # Accessed from tail + +# Convert to vectors +forward_vector <- dll$to_vector() +backward_vector <- dll$to_vector_reverse() +``` + +#### Circular Linked List +```r +source("circular_linked_list.r") + +# Create new circular linked list +cll <- CircularLinkedList$new() + +# Build the circle +cll$insert_at_tail(1) +cll$insert_at_tail(2) +cll$insert_at_tail(3) + +# Demonstrate circular nature +cll$traverse_n_times(2) # Go around circle twice + +# Solve classic problems +survivor <- cll$josephus_problem(3) # Josephus problem with k=3 +``` + +## 🎯 Advanced Features + +### Comprehensive Operations +All implementations include: +- **Insertion**: at head, tail, or any position +- **Deletion**: from head, tail, position, or by value +- **Search**: find element position +- **Access**: get element by position +- **Utilities**: size, empty check, clear, convert to vector + +### Algorithm Implementations +- **Middle element detection** (two-pointer technique) +- **Cycle detection** (Floyd's algorithm) +- **List reversal** (in-place) +- **Josephus problem** (circular list specialty) + +### Educational Features +- **Step-by-step explanations** with console output +- **Time/space complexity analysis** in comments +- **Real-world examples** and use cases +- **Interactive demonstrations** for each type + +## 🔬 Detailed Examples + +### Running the Demonstrations +Each implementation includes comprehensive examples: + +```r +# Singly Linked List examples +source("singly_linked_list.r") +demonstrate_singly_linked_list() + +# Doubly Linked List examples +source("doubly_linked_list.r") +demonstrate_doubly_linked_list() + +# Circular Linked List examples +source("circular_linked_list.r") +demonstrate_circular_linked_list() +``` + +### Real-World Applications + +#### 1. Student Grade Management (Singly Linked List) +```r +grades_list <- SinglyLinkedList$new() +grades <- c(85, 92, 78, 96, 83, 89) + +# Add grades +for (grade in grades) { + grades_list$insert_at_tail(grade) +} + +# Calculate statistics +grade_vector <- grades_list$to_vector() +average <- mean(grade_vector) +``` + +#### 2. Browser History (Doubly Linked List) +```r +history <- DoublyLinkedList$new() + +# Visit pages +history$insert_at_tail("google.com") +history$insert_at_tail("github.com") +history$insert_at_tail("stackoverflow.com") + +# Go back (delete current) +current_page <- history$delete_from_tail() + +# Go forward (add new page) +history$insert_at_tail("wikipedia.org") +``` + +#### 3. Round-Robin Scheduling (Circular Linked List) +```r +scheduler <- CircularLinkedList$new() + +# Add processes +processes <- c("Process_A", "Process_B", "Process_C") +for (process in processes) { + scheduler$insert_at_tail(process) +} + +# Simulate time slots +current <- scheduler$head +for (slot in 1:6) { + cat("Time slot", slot, ":", current$data, "\n") + current <- current$next_node +} +``` + +## 🧮 Algorithm Analysis + +### Space Complexity +- **Singly**: O(n) space, 1 pointer per node +- **Doubly**: O(n) space, 2 pointers per node +- **Circular**: O(n) space, 1 pointer per node + +### When to Use Each Type + +| Requirement | Best Choice | Reason | +|-------------|-------------|---------| +| Memory efficiency | Singly | Minimal pointer overhead | +| Bidirectional traversal | Doubly | Two-way navigation | +| Cyclic operations | Circular | Natural circular structure | +| Frequent tail operations | Doubly | O(1) tail access | +| Simple implementation | Singly | Easiest to understand/debug | +| LRU Cache | Doubly | Efficient insertion/deletion anywhere | +| Round-robin algorithms | Circular | Natural fit for cyclic scheduling | + +## ⚠️ Important Considerations + +### Common Pitfalls +1. **Memory Leaks**: Ensure proper node cleanup in languages with manual memory management +2. **Infinite Loops**: In circular lists, always track traversal completion +3. **Null Pointer Exceptions**: Check for empty lists before operations +4. **Position Bounds**: Validate position parameters in insert/delete operations + +### Best Practices +1. **Always validate inputs** (positions, null checks) +2. **Maintain size counter** for O(1) size queries +3. **Use appropriate type** based on access patterns +4. **Test edge cases** (empty list, single element, position bounds) +5. **Document time complexities** for each operation + +## 📚 Further Reading + +### Recommended Algorithms to Implement Next +- **Stack using Linked List**: LIFO operations +- **Queue using Linked List**: FIFO operations +- **Deque using Doubly Linked List**: Double-ended queue +- **LRU Cache using Doubly Linked List**: Cache replacement policy +- **Skip List**: Probabilistic data structure for fast search + +### Advanced Topics +- **Lock-free Linked Lists**: Concurrent programming +- **Memory Pool Allocation**: Efficient node management +- **Template/Generic Implementations**: Type-safe containers +- **Persistent Linked Lists**: Functional programming approach + +## 🤝 Contributing + +We welcome contributions! Consider adding: +- Additional linked list variants (XOR linked list, skip list) +- Performance benchmarking utilities +- Visualization functions (ASCII art display) +- More real-world application examples +- Memory usage analysis tools + +### Development Guidelines +1. Follow existing code style and documentation standards +2. Include comprehensive test cases and examples +3. Add time/space complexity analysis in comments +4. Provide practical application demonstrations +5. Ensure no automatic side effects (examples available but not auto-run) + +## 📄 License + +This code is provided for educational purposes. Please cite appropriately if used in academic work. + +--- + +*"A linked list is a linear collection of data elements whose order is not given by their physical placement in memory."* + +These implementations provide a solid foundation for understanding linked lists and can serve as building blocks for more complex data structures and algorithms! \ No newline at end of file diff --git a/linked_list_algorithms/circular_linked_list.r b/linked_list_algorithms/circular_linked_list.r new file mode 100644 index 00000000..f3e3af1d --- /dev/null +++ b/linked_list_algorithms/circular_linked_list.r @@ -0,0 +1,628 @@ +# Circular Linked List Implementation in R +# +# A circular linked list is a variation of linked list where the last node +# points back to the first node, forming a circle. There's no NULL pointer +# at the end, making it truly circular. +# +# Types: +# - Circular Singly Linked List: Last node points to first node +# - Circular Doubly Linked List: Last node points to first, first points to last +# +# Time Complexities: +# - Access: O(n) - must traverse from any starting point +# - Search: O(n) - linear search through nodes +# - Insertion: O(1) at known position, O(n) at arbitrary position +# - Deletion: O(1) at known position, O(n) at arbitrary position +# +# Space Complexity: O(n) where n is number of elements +# +# Advantages: +# - Can start from any node and traverse entire list +# - Useful for round-robin scheduling +# - Efficient for applications needing cyclic traversal +# - No NULL pointers (except when empty) +# +# Disadvantages: +# - Risk of infinite loops if not handled carefully +# - Slightly more complex implementation +# - Detection of end requires careful tracking +# +# Applications: +# - Round-robin CPU scheduling +# - Multiplayer games (turn-based) +# - Circular buffer implementation +# - Music playlist (continuous loop) +# - Josephus problem solution + +# Define Node class for circular linked list +CircularNode <- setRefClass("CircularNode", + fields = list( + data = "ANY", + next_node = "ANY" + ), + methods = list( + initialize = function(data = NULL, next_node = NULL) { + .self$data <- data + .self$next_node <- next_node + }, + + print = function() { + next_data <- if(is.null(.self$next_node)) "NULL" else .self$next_node$data + cat("CircularNode(data =", .self$data, ", next =", next_data, ")\n") + } + ) +) + +# Define Circular Linked List class +CircularLinkedList <- setRefClass("CircularLinkedList", + fields = list( + head = "ANY", + size = "numeric" + ), + methods = list( + initialize = function() { + .self$head <- NULL + .self$size <- 0 + }, + + # Insert element at the beginning of the list + insert_at_head = function(data) { + "Insert a new node at the beginning of the list" + new_node <- CircularNode$new(data = data) + + if (is.null(.self$head)) { + # First node - points to itself + new_node$next_node <- new_node + .self$head <- new_node + } else { + # Find the last node (points to current head) + last_node <- .self$head + while (!identical(last_node$next_node, .self$head)) { + last_node <- last_node$next_node + } + + new_node$next_node <- .self$head + last_node$next_node <- new_node + .self$head <- new_node + } + + .self$size <- .self$size + 1 + cat("Inserted", data, "at head. List size:", .self$size, "\n") + }, + + # Insert element at the end of the list + insert_at_tail = function(data) { + "Insert a new node at the end of the list" + if (is.null(.self$head)) { + .self$insert_at_head(data) + return() + } + + new_node <- CircularNode$new(data = data) + + # Find the last node + last_node <- .self$head + while (!identical(last_node$next_node, .self$head)) { + last_node <- last_node$next_node + } + + new_node$next_node <- .self$head + last_node$next_node <- new_node + + .self$size <- .self$size + 1 + cat("Inserted", data, "at tail. List size:", .self$size, "\n") + }, + + # Insert element at specified position + insert_at_position = function(data, position) { + "Insert a new node at the specified position (0-indexed)" + if (position < 0 || position > .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size) + } + + if (position == 0) { + .self$insert_at_head(data) + return() + } + + new_node <- CircularNode$new(data = data) + current <- .self$head + + # Traverse to position - 1 + for (i in 1:(position - 1)) { + current <- current$next_node + } + + new_node$next_node <- current$next_node + current$next_node <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at position", position, ". List size:", .self$size, "\n") + }, + + # Delete element from the beginning + delete_from_head = function() { + "Remove and return the first element" + if (is.null(.self$head)) { + stop("Cannot delete from empty list") + } + + deleted_data <- .self$head$data + + if (.self$size == 1) { + .self$head <- NULL + } else { + # Find the last node + last_node <- .self$head + while (!identical(last_node$next_node, .self$head)) { + last_node <- last_node$next_node + } + + last_node$next_node <- .self$head$next_node + .self$head <- .self$head$next_node + } + + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from head. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element from the end + delete_from_tail = function() { + "Remove and return the last element" + if (is.null(.self$head)) { + stop("Cannot delete from empty list") + } + + if (.self$size == 1) { + return(.self$delete_from_head()) + } + + # Find second-to-last node + current <- .self$head + while (!identical(current$next_node$next_node, .self$head)) { + current <- current$next_node + } + + deleted_data <- current$next_node$data + current$next_node <- .self$head + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from tail. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element at specified position + delete_at_position = function(position) { + "Remove and return element at specified position (0-indexed)" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + if (position == 0) { + return(.self$delete_from_head()) + } + + current <- .self$head + # Traverse to position - 1 + for (i in 1:(position - 1)) { + current <- current$next_node + } + + deleted_data <- current$next_node$data + current$next_node <- current$next_node$next_node + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "at position", position, ". List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete by value (first occurrence) + delete_by_value = function(value) { + "Remove first occurrence of specified value" + if (is.null(.self$head)) { + cat("List is empty, nothing to delete\n") + return(FALSE) + } + + # Check if head contains the value + if (.self$head$data == value) { + .self$delete_from_head() + return(TRUE) + } + + current <- .self$head + repeat { + if (current$next_node$data == value) { + current$next_node <- current$next_node$next_node + .self$size <- .self$size - 1 + cat("Deleted first occurrence of", value, ". List size:", .self$size, "\n") + return(TRUE) + } + current <- current$next_node + + # Check if we've completed the circle + if (identical(current, .self$head)) { + break + } + } + + cat("Value", value, "not found in list\n") + return(FALSE) + }, + + # Search for an element + search = function(value) { + "Search for a value and return its position (0-indexed), -1 if not found" + if (is.null(.self$head)) { + cat("List is empty\n") + return(-1) + } + + current <- .self$head + position <- 0 + + repeat { + if (current$data == value) { + cat("Found", value, "at position", position, "\n") + return(position) + } + current <- current$next_node + position <- position + 1 + + # Check if we've completed the circle + if (identical(current, .self$head)) { + break + } + } + + cat("Value", value, "not found in list\n") + return(-1) + }, + + # Get element at specified position + get = function(position) { + "Get element at specified position without removing it" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + current <- .self$head + for (i in 1:position) { + current <- current$next_node + } + + return(current$data) + }, + + # Get the size of the list + get_size = function() { + "Return the number of elements in the list" + return(.self$size) + }, + + # Check if the list is empty + is_empty = function() { + "Check if the list is empty" + return(.self$size == 0) + }, + + # Convert list to R vector + to_vector = function() { + "Convert circular linked list to R vector" + if (.self$is_empty()) { + return(c()) + } + + result <- c() + current <- .self$head + + repeat { + result <- c(result, current$data) + current <- current$next_node + + # Stop when we complete the circle + if (identical(current, .self$head)) { + break + } + } + + return(result) + }, + + # Traverse the list n times (demonstrating circular nature) + traverse_n_times = function(n_cycles = 2) { + "Demonstrate circular traversal by going around the list multiple times" + if (.self$is_empty()) { + cat("List is empty\n") + return() + } + + cat("Traversing circular list", n_cycles, "times:\n") + current <- .self$head + total_visits <- n_cycles * .self$size + + for (i in 1:total_visits) { + cat(current$data) + current <- current$next_node + + if (i < total_visits) { + cat(" -> ") + } + + # Add cycle markers + if (i %% .self$size == 0) { + cat(" [Cycle ", i / .self$size, " complete]") + if (i < total_visits) { + cat(" -> ") + } + } + } + cat("\n") + }, + + # Solve Josephus problem using circular linked list + josephus_problem = function(k) { + "Solve Josephus problem: eliminate every k-th person until one remains" + if (.self$is_empty()) { + cat("List is empty\n") + return(NULL) + } + + if (k <= 0) { + stop("k must be positive") + } + + cat("Solving Josephus problem with k =", k, "\n") + cat("Initial people:", .self$to_vector(), "\n") + + current <- .self$head + + while (.self$size > 1) { + # Move k-1 positions + for (i in 1:(k - 1)) { + current <- current$next_node + } + + # Eliminate current person + eliminated <- current$data + cat("Eliminating person:", eliminated, "\n") + + # Find the position and delete + position <- 0 + temp <- .self$head + while (!identical(temp, current)) { + temp <- temp$next_node + position <- position + 1 + } + + # Move to next person before deletion + next_person <- current$next_node + .self$delete_at_position(position) + + # Update current to next person, but handle head change + if (.self$size > 0) { + if (position == 0) { + current <- .self$head + } else { + current <- next_person + } + } + } + + survivor <- .self$head$data + cat("Survivor:", survivor, "\n") + return(survivor) + }, + + # Print the entire list + print_list = function() { + "Print all elements in the circular list" + if (.self$is_empty()) { + cat("List is empty\n") + return() + } + + cat("Circular Linked List: ") + current <- .self$head + elements <- c() + + repeat { + elements <- c(elements, current$data) + current <- current$next_node + + if (identical(current, .self$head)) { + break + } + } + + cat(paste(elements, collapse = " -> "), "-> (back to", .self$head$data, ")\n") + cat("Size:", .self$size, "\n") + }, + + # Clear the entire list + clear = function() { + "Remove all elements from the list" + .self$head <- NULL + .self$size <- 0 + cat("List cleared\n") + } + ) +) + +# ============================================================================== +# EXAMPLES AND DEMONSTRATIONS +# ============================================================================== + +demonstrate_circular_linked_list <- function() { + cat("=================================================================\n") + cat("CIRCULAR LINKED LIST - COMPREHENSIVE DEMONSTRATION\n") + cat("=================================================================\n\n") + + # Create a new circular linked list + cat("1. CREATING A NEW CIRCULAR LINKED LIST\n") + cat("-----------------------------------------------------------------\n") + cll <- CircularLinkedList$new() + cat("Created empty circular linked list\n") + cll$print_list() + + cat("\n2. INSERTION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Insert at head + cat("Inserting elements at head: 10, 20, 30\n") + cll$insert_at_head(10) + cll$insert_at_head(20) + cll$insert_at_head(30) + cll$print_list() + + # Insert at tail + cat("\nInserting elements at tail: 5, 1\n") + cll$insert_at_tail(5) + cll$insert_at_tail(1) + cll$print_list() + + # Insert at specific position + cat("\nInserting 15 at position 2\n") + cll$insert_at_position(15, 2) + cll$print_list() + + cat("\n3. CIRCULAR TRAVERSAL DEMONSTRATION\n") + cat("-----------------------------------------------------------------\n") + + # Demonstrate circular nature + cat("Normal traversal (one complete cycle):\n") + cll$print_list() + + cat("\nDemonstrating circular nature (2 complete cycles):\n") + cll$traverse_n_times(2) + + cat("\n4. ACCESS AND SEARCH OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Search for elements + cat("Searching for elements:\n") + cll$search(15) + cll$search(99) + + # Get elements by position + cat("\nAccessing elements by position:\n") + cat("Element at position 0:", cll$get(0), "\n") + cat("Element at position 3:", cll$get(3), "\n") + + cat("\n5. DELETION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Delete from head + cat("Deleting from head:\n") + deleted <- cll$delete_from_head() + cat("Deleted value:", deleted, "\n") + cll$print_list() + + # Delete from tail + cat("\nDeleting from tail:\n") + deleted <- cll$delete_from_tail() + cat("Deleted value:", deleted, "\n") + cll$print_list() + + # Delete by value + cat("\nDeleting by value (15):\n") + cll$delete_by_value(15) + cll$print_list() + + cat("\n6. JOSEPHUS PROBLEM DEMONSTRATION\n") + cat("-----------------------------------------------------------------\n") + + # Create a new list for Josephus problem + josephus_list <- CircularLinkedList$new() + + cat("Creating a circle of 7 people for Josephus problem:\n") + for (i in 1:7) { + josephus_list$insert_at_tail(i) + } + josephus_list$print_list() + + cat("\nSolving Josephus problem with k = 3 (eliminate every 3rd person):\n") + survivor <- josephus_list$josephus_problem(3) + cat("Final survivor:", survivor, "\n") + + cat("\n7. PRACTICAL EXAMPLE: ROUND-ROBIN SCHEDULING\n") + cat("-----------------------------------------------------------------\n") + + # Simulate round-robin CPU scheduling + scheduler <- CircularLinkedList$new() + + processes <- c("Process_A", "Process_B", "Process_C", "Process_D") + cat("Setting up round-robin scheduler with processes:\n") + for (process in processes) { + scheduler$insert_at_tail(process) + cat("Added:", process, "\n") + } + + scheduler$print_list() + + cat("\nSimulating 8 time slots of round-robin scheduling:\n") + if (!scheduler$is_empty()) { + current_process <- scheduler$head + for (slot in 1:8) { + cat("Time slot", slot, ": Running", current_process$data, "\n") + current_process <- current_process$next_node + } + } + + cat("\n8. PRACTICAL EXAMPLE: MUSIC PLAYLIST\n") + cat("-----------------------------------------------------------------\n") + + # Create a circular music playlist + playlist <- CircularLinkedList$new() + + songs <- c("Song1.mp3", "Song2.mp3", "Song3.mp3", "Song4.mp3") + cat("Creating circular music playlist:\n") + for (song in songs) { + playlist$insert_at_tail(song) + cat("Added:", song, "\n") + } + + playlist$print_list() + + cat("\nPlaylist in repeat mode (playing 6 songs with wraparound):\n") + if (!playlist$is_empty()) { + current_song <- playlist$head + for (play_count in 1:6) { + cat("Now playing:", current_song$data, "\n") + current_song <- current_song$next_node + } + } + + cat("\n9. ADVANTAGES DEMONSTRATION\n") + cat("-----------------------------------------------------------------\n") + + cat("Circular linked list advantages demonstrated:\n") + cat("- Continuous traversal without NULL checks\n") + cat("- Perfect for cyclic applications (scheduling, playlists)\n") + cat("- Efficient round-robin algorithms\n") + cat("- Elegant solution to Josephus problem\n") + cat("- No need to track end of list in many algorithms\n") + + cat("\n=================================================================\n") + cat("END OF CIRCULAR LINKED LIST DEMONSTRATION\n") + cat("=================================================================\n") +} + +# Helper function to create a circular linked list from vector +create_circular_list_from_vector <- function(vector_data) { + "Create a circular linked list from an R vector" + new_list <- CircularLinkedList$new() + for (element in vector_data) { + new_list$insert_at_tail(element) + } + return(new_list) +} + +# Examples are available but not run automatically to avoid side effects +# To run examples, execute: demonstrate_circular_linked_list() +if (interactive()) { + cat("Loading Circular Linked List implementation...\n") + cat("Run 'demonstrate_circular_linked_list()' to see comprehensive examples.\n") + cat("Use 'CircularLinkedList$new()' to create a new circular linked list.\n") +} + +# Uncomment the following line to run examples automatically: +# demonstrate_circular_linked_list() \ No newline at end of file diff --git a/linked_list_algorithms/doubly_linked_list.r b/linked_list_algorithms/doubly_linked_list.r new file mode 100644 index 00000000..0b79d868 --- /dev/null +++ b/linked_list_algorithms/doubly_linked_list.r @@ -0,0 +1,592 @@ +# Doubly Linked List Implementation in R +# +# A doubly linked list is a linear data structure where each node contains data +# and two references: one to the next node and one to the previous node. +# This bidirectional linking allows traversal in both directions. +# +# Time Complexities: +# - Access: O(n) - must traverse from head or tail +# - Search: O(n) - linear search through nodes +# - Insertion: O(1) at head/tail, O(n) at arbitrary position +# - Deletion: O(1) at head/tail, O(n) at arbitrary position +# +# Space Complexity: O(n) where n is number of elements +# +# Advantages over Singly Linked List: +# - Bidirectional traversal (forward and backward) +# - Easier deletion (no need to track previous node) +# - Better for certain algorithms (e.g., LRU cache) +# +# Disadvantages: +# - Extra memory overhead for storing previous pointer +# - More complex implementation +# - Slightly slower insertion/deletion due to extra pointer updates +# +# Applications: +# - Browser history (back/forward navigation) +# - Undo/Redo operations +# - LRU (Least Recently Used) cache implementation +# - Music player (previous/next song) +# - Navigation in applications + +# Define Node class for doubly linked list +DoublyNode <- setRefClass("DoublyNode", + fields = list( + data = "ANY", + next_node = "ANY", + prev_node = "ANY" + ), + methods = list( + initialize = function(data = NULL, next_node = NULL, prev_node = NULL) { + .self$data <- data + .self$next_node <- next_node + .self$prev_node <- prev_node + }, + + print = function() { + prev_data <- if(is.null(.self$prev_node)) "NULL" else .self$prev_node$data + next_data <- if(is.null(.self$next_node)) "NULL" else .self$next_node$data + cat("DoublyNode(prev =", prev_data, ", data =", .self$data, ", next =", next_data, ")\n") + } + ) +) + +# Define Doubly Linked List class +DoublyLinkedList <- setRefClass("DoublyLinkedList", + fields = list( + head = "ANY", + tail = "ANY", + size = "numeric" + ), + methods = list( + initialize = function() { + .self$head <- NULL + .self$tail <- NULL + .self$size <- 0 + }, + + # Insert element at the beginning of the list + insert_at_head = function(data) { + "Insert a new node at the beginning of the list" + new_node <- DoublyNode$new(data = data, next_node = .self$head, prev_node = NULL) + + if (!is.null(.self$head)) { + .self$head$prev_node <- new_node + } else { + # First node, also becomes tail + .self$tail <- new_node + } + + .self$head <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at head. List size:", .self$size, "\n") + }, + + # Insert element at the end of the list + insert_at_tail = function(data) { + "Insert a new node at the end of the list" + new_node <- DoublyNode$new(data = data, next_node = NULL, prev_node = .self$tail) + + if (!is.null(.self$tail)) { + .self$tail$next_node <- new_node + } else { + # First node, also becomes head + .self$head <- new_node + } + + .self$tail <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at tail. List size:", .self$size, "\n") + }, + + # Insert element at specified position + insert_at_position = function(data, position) { + "Insert a new node at the specified position (0-indexed)" + if (position < 0 || position > .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size) + } + + if (position == 0) { + .self$insert_at_head(data) + return() + } + + if (position == .self$size) { + .self$insert_at_tail(data) + return() + } + + # Determine whether to traverse from head or tail for efficiency + if (position <= .self$size / 2) { + # Traverse from head + current <- .self$head + for (i in 1:position) { + current <- current$next_node + } + } else { + # Traverse from tail + current <- .self$tail + for (i in 1:(.self$size - position)) { + current <- current$prev_node + } + } + + new_node <- DoublyNode$new(data = data, next_node = current, prev_node = current$prev_node) + current$prev_node$next_node <- new_node + current$prev_node <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at position", position, ". List size:", .self$size, "\n") + }, + + # Delete element from the beginning + delete_from_head = function() { + "Remove and return the first element" + if (is.null(.self$head)) { + stop("Cannot delete from empty list") + } + + deleted_data <- .self$head$data + + if (.self$size == 1) { + .self$head <- NULL + .self$tail <- NULL + } else { + .self$head <- .self$head$next_node + .self$head$prev_node <- NULL + } + + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from head. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element from the end + delete_from_tail = function() { + "Remove and return the last element" + if (is.null(.self$tail)) { + stop("Cannot delete from empty list") + } + + deleted_data <- .self$tail$data + + if (.self$size == 1) { + .self$head <- NULL + .self$tail <- NULL + } else { + .self$tail <- .self$tail$prev_node + .self$tail$next_node <- NULL + } + + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from tail. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element at specified position + delete_at_position = function(position) { + "Remove and return element at specified position (0-indexed)" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + if (position == 0) { + return(.self$delete_from_head()) + } + + if (position == .self$size - 1) { + return(.self$delete_from_tail()) + } + + # Determine whether to traverse from head or tail for efficiency + if (position <= .self$size / 2) { + current <- .self$head + for (i in 1:position) { + current <- current$next_node + } + } else { + current <- .self$tail + for (i in 1:(.self$size - 1 - position)) { + current <- current$prev_node + } + } + + deleted_data <- current$data + current$prev_node$next_node <- current$next_node + current$next_node$prev_node <- current$prev_node + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "at position", position, ". List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete by value (first occurrence) + delete_by_value = function(value) { + "Remove first occurrence of specified value" + if (is.null(.self$head)) { + cat("List is empty, nothing to delete\n") + return(FALSE) + } + + current <- .self$head + while (!is.null(current)) { + if (current$data == value) { + if (identical(current, .self$head)) { + .self$delete_from_head() + } else if (identical(current, .self$tail)) { + .self$delete_from_tail() + } else { + current$prev_node$next_node <- current$next_node + current$next_node$prev_node <- current$prev_node + .self$size <- .self$size - 1 + cat("Deleted first occurrence of", value, ". List size:", .self$size, "\n") + } + return(TRUE) + } + current <- current$next_node + } + + cat("Value", value, "not found in list\n") + return(FALSE) + }, + + # Search for an element + search = function(value) { + "Search for a value and return its position (0-indexed), -1 if not found" + current <- .self$head + position <- 0 + + while (!is.null(current)) { + if (current$data == value) { + cat("Found", value, "at position", position, "\n") + return(position) + } + current <- current$next_node + position <- position + 1 + } + + cat("Value", value, "not found in list\n") + return(-1) + }, + + # Get element at specified position + get = function(position) { + "Get element at specified position without removing it" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + # Optimize traversal by choosing direction + if (position <= .self$size / 2) { + current <- .self$head + for (i in 1:position) { + current <- current$next_node + } + } else { + current <- .self$tail + for (i in 1:(.self$size - 1 - position)) { + current <- current$prev_node + } + } + + return(current$data) + }, + + # Get the size of the list + get_size = function() { + "Return the number of elements in the list" + return(.self$size) + }, + + # Check if the list is empty + is_empty = function() { + "Check if the list is empty" + return(.self$size == 0) + }, + + # Convert list to R vector (forward direction) + to_vector = function() { + "Convert linked list to R vector" + if (.self$is_empty()) { + return(c()) + } + + result <- c() + current <- .self$head + while (!is.null(current)) { + result <- c(result, current$data) + current <- current$next_node + } + return(result) + }, + + # Convert list to R vector (backward direction) + to_vector_reverse = function() { + "Convert linked list to R vector in reverse order" + if (.self$is_empty()) { + return(c()) + } + + result <- c() + current <- .self$tail + while (!is.null(current)) { + result <- c(result, current$data) + current <- current$prev_node + } + return(result) + }, + + # Reverse the linked list + reverse = function() { + "Reverse the doubly linked list in place" + if (.self$is_empty() || .self$size == 1) { + return() + } + + current <- .self$head + temp <- NULL + + # Swap next and prev for all nodes + while (!is.null(current)) { + temp <- current$prev_node + current$prev_node <- current$next_node + current$next_node <- temp + current <- current$prev_node # Move to next node (was prev due to swap) + } + + # Swap head and tail + temp <- .self$head + .self$head <- .self$tail + .self$tail <- temp + + cat("List reversed successfully\n") + }, + + # Print the entire list (forward) + print_list = function() { + "Print all elements in the list (forward direction)" + if (.self$is_empty()) { + cat("List is empty\n") + return() + } + + cat("Doubly Linked List (Forward): NULL <- ") + current <- .self$head + elements <- c() + + while (!is.null(current)) { + elements <- c(elements, current$data) + current <- current$next_node + } + + cat(paste(elements, collapse = " <-> "), " -> NULL\n") + cat("Size:", .self$size, "\n") + }, + + # Print the entire list (backward) + print_list_reverse = function() { + "Print all elements in the list (backward direction)" + if (.self$is_empty()) { + cat("List is empty\n") + return() + } + + cat("Doubly Linked List (Backward): NULL <- ") + current <- .self$tail + elements <- c() + + while (!is.null(current)) { + elements <- c(elements, current$data) + current <- current$prev_node + } + + cat(paste(elements, collapse = " <-> "), " -> NULL\n") + cat("Size:", .self$size, "\n") + }, + + # Clear the entire list + clear = function() { + "Remove all elements from the list" + .self$head <- NULL + .self$tail <- NULL + .self$size <- 0 + cat("List cleared\n") + } + ) +) + +# ============================================================================== +# EXAMPLES AND DEMONSTRATIONS +# ============================================================================== + +demonstrate_doubly_linked_list <- function() { + cat("=================================================================\n") + cat("DOUBLY LINKED LIST - COMPREHENSIVE DEMONSTRATION\n") + cat("=================================================================\n\n") + + # Create a new doubly linked list + cat("1. CREATING A NEW DOUBLY LINKED LIST\n") + cat("-----------------------------------------------------------------\n") + dll <- DoublyLinkedList$new() + cat("Created empty doubly linked list\n") + dll$print_list() + + cat("\n2. INSERTION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Insert at head + cat("Inserting elements at head: 10, 20, 30\n") + dll$insert_at_head(10) + dll$insert_at_head(20) + dll$insert_at_head(30) + dll$print_list() + + # Insert at tail + cat("\nInserting elements at tail: 5, 1\n") + dll$insert_at_tail(5) + dll$insert_at_tail(1) + dll$print_list() + + # Insert at specific position + cat("\nInserting 15 at position 2\n") + dll$insert_at_position(15, 2) + dll$print_list() + + cat("\n3. BIDIRECTIONAL TRAVERSAL\n") + cat("-----------------------------------------------------------------\n") + + cat("Forward traversal:\n") + dll$print_list() + + cat("\nBackward traversal:\n") + dll$print_list_reverse() + + cat("\nForward vector:", dll$to_vector(), "\n") + cat("Backward vector:", dll$to_vector_reverse(), "\n") + + cat("\n4. ACCESS AND SEARCH OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Search for elements + cat("Searching for elements:\n") + dll$search(15) + dll$search(99) + + # Get elements by position (optimized traversal) + cat("\nAccessing elements by position (optimized traversal):\n") + cat("Element at position 0:", dll$get(0), "\n") + cat("Element at position", dll$get_size() - 1, ":", dll$get(dll$get_size() - 1), "\n") + + cat("\n5. DELETION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Delete from head + cat("Deleting from head:\n") + deleted <- dll$delete_from_head() + cat("Deleted value:", deleted, "\n") + dll$print_list() + + # Delete from tail + cat("\nDeleting from tail:\n") + deleted <- dll$delete_from_tail() + cat("Deleted value:", deleted, "\n") + dll$print_list() + + # Delete by value + cat("\nDeleting by value (15):\n") + dll$delete_by_value(15) + dll$print_list() + + cat("\n6. ADVANCED OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Add more elements for demonstration + cat("Adding more elements for advanced operations:\n") + dll$insert_at_tail(100) + dll$insert_at_tail(200) + dll$insert_at_tail(300) + dll$print_list() + + # Reverse the list + cat("\nReversing the list:\n") + dll$reverse() + dll$print_list() + cat("After reverse - backward traversal:\n") + dll$print_list_reverse() + + cat("\n7. PRACTICAL EXAMPLE: BROWSER HISTORY\n") + cat("-----------------------------------------------------------------\n") + + # Simulate browser history using doubly linked list + browser_history <- DoublyLinkedList$new() + + cat("Simulating browser history navigation:\n") + websites <- c("google.com", "github.com", "stackoverflow.com", "reddit.com") + + cat("Visiting websites:\n") + for (site in websites) { + browser_history$insert_at_tail(site) + cat("Visited:", site, "\n") + } + + cat("\nCurrent history (chronological order):\n") + browser_history$print_list() + + cat("\nCurrent history (reverse chronological order):\n") + browser_history$print_list_reverse() + + # Simulate going back + cat("\nGoing back (removing current page):\n") + current_page <- browser_history$delete_from_tail() + cat("Left page:", current_page, "\n") + cat("Now at:", browser_history$get(browser_history$get_size() - 1), "\n") + + # Add new page (forward navigation) + cat("\nNavigating to new page: wikipedia.org\n") + browser_history$insert_at_tail("wikipedia.org") + browser_history$print_list() + + cat("\n8. PERFORMANCE COMPARISON\n") + cat("-----------------------------------------------------------------\n") + + cat("Doubly linked list advantages demonstrated:\n") + cat("- Bidirectional traversal: O(1) access to both head and tail\n") + cat("- Optimized position access: traverse from closer end\n") + cat("- Easier deletion: no need to track previous node\n") + + # Demonstrate optimized access + cat("\nOptimized access example (large list):\n") + large_dll <- DoublyLinkedList$new() + for (i in 1:10) { + large_dll$insert_at_tail(i * 10) + } + + cat("Accessing element near beginning (position 2):\n") + cat("Element:", large_dll$get(2), "(traversed from head)\n") + + cat("Accessing element near end (position 8):\n") + cat("Element:", large_dll$get(8), "(traversed from tail)\n") + + cat("\n=================================================================\n") + cat("END OF DOUBLY LINKED LIST DEMONSTRATION\n") + cat("=================================================================\n") +} + +# Helper function to create a doubly linked list from vector +create_doubly_list_from_vector <- function(vector_data) { + "Create a doubly linked list from an R vector" + new_list <- DoublyLinkedList$new() + for (element in vector_data) { + new_list$insert_at_tail(element) + } + return(new_list) +} + +# Examples are available but not run automatically to avoid side effects +# To run examples, execute: demonstrate_doubly_linked_list() +if (interactive()) { + cat("Loading Doubly Linked List implementation...\n") + cat("Run 'demonstrate_doubly_linked_list()' to see comprehensive examples.\n") + cat("Use 'DoublyLinkedList$new()' to create a new doubly linked list.\n") +} + +# Uncomment the following line to run examples automatically: +# demonstrate_doubly_linked_list() \ No newline at end of file diff --git a/linked_list_algorithms/singly_linked_list.r b/linked_list_algorithms/singly_linked_list.r new file mode 100644 index 00000000..defc027c --- /dev/null +++ b/linked_list_algorithms/singly_linked_list.r @@ -0,0 +1,528 @@ +# Singly Linked List Implementation in R +# +# A singly linked list is a linear data structure where each element (node) contains +# data and a reference (pointer) to the next node in the sequence. Unlike arrays, +# linked lists don't store elements in contiguous memory locations. +# +# Time Complexities: +# - Access: O(n) - must traverse from head to reach element +# - Search: O(n) - linear search through nodes +# - Insertion: O(1) at head, O(n) at arbitrary position +# - Deletion: O(1) at head, O(n) at arbitrary position +# +# Space Complexity: O(n) where n is number of elements +# +# Advantages: +# - Dynamic size (can grow/shrink during runtime) +# - Efficient insertion/deletion at beginning +# - Memory efficient (allocates memory as needed) +# +# Disadvantages: +# - No random access (must traverse from head) +# - Extra memory overhead for storing pointers +# - Not cache-friendly due to non-contiguous memory +# +# Applications: +# - Implementation of stacks and queues +# - Undo functionality in applications +# - Music playlist (next song) +# - Browser history navigation + +# Define Node class for singly linked list +SinglyNode <- setRefClass("SinglyNode", + fields = list( + data = "ANY", + next_node = "ANY" + ), + methods = list( + initialize = function(data = NULL, next_node = NULL) { + .self$data <- data + .self$next_node <- next_node + }, + + print = function() { + cat("Node(data =", .self$data, ")\n") + } + ) +) + +# Define Singly Linked List class +SinglyLinkedList <- setRefClass("SinglyLinkedList", + fields = list( + head = "ANY", + size = "numeric" + ), + methods = list( + initialize = function() { + .self$head <- NULL + .self$size <- 0 + }, + + # Insert element at the beginning of the list + insert_at_head = function(data) { + "Insert a new node at the beginning of the list" + new_node <- SinglyNode$new(data = data, next_node = .self$head) + .self$head <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at head. List size:", .self$size, "\n") + }, + + # Insert element at the end of the list + insert_at_tail = function(data) { + "Insert a new node at the end of the list" + new_node <- SinglyNode$new(data = data, next_node = NULL) + + if (is.null(.self$head)) { + .self$head <- new_node + } else { + current <- .self$head + while (!is.null(current$next_node)) { + current <- current$next_node + } + current$next_node <- new_node + } + .self$size <- .self$size + 1 + cat("Inserted", data, "at tail. List size:", .self$size, "\n") + }, + + # Insert element at specified position + insert_at_position = function(data, position) { + "Insert a new node at the specified position (0-indexed)" + if (position < 0 || position > .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size) + } + + if (position == 0) { + .self$insert_at_head(data) + return() + } + + new_node <- SinglyNode$new(data = data) + current <- .self$head + + # Traverse to position - 1 + for (i in 1:(position - 1)) { + current <- current$next_node + } + + new_node$next_node <- current$next_node + current$next_node <- new_node + .self$size <- .self$size + 1 + cat("Inserted", data, "at position", position, ". List size:", .self$size, "\n") + }, + + # Delete element from the beginning + delete_from_head = function() { + "Remove and return the first element" + if (is.null(.self$head)) { + stop("Cannot delete from empty list") + } + + deleted_data <- .self$head$data + .self$head <- .self$head$next_node + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from head. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element from the end + delete_from_tail = function() { + "Remove and return the last element" + if (is.null(.self$head)) { + stop("Cannot delete from empty list") + } + + if (is.null(.self$head$next_node)) { + # Only one element + deleted_data <- .self$head$data + .self$head <- NULL + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from tail. List size:", .self$size, "\n") + return(deleted_data) + } + + # Find second-to-last node + current <- .self$head + while (!is.null(current$next_node$next_node)) { + current <- current$next_node + } + + deleted_data <- current$next_node$data + current$next_node <- NULL + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "from tail. List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete element at specified position + delete_at_position = function(position) { + "Remove and return element at specified position (0-indexed)" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + if (position == 0) { + return(.self$delete_from_head()) + } + + current <- .self$head + # Traverse to position - 1 + for (i in 1:(position - 1)) { + current <- current$next_node + } + + deleted_data <- current$next_node$data + current$next_node <- current$next_node$next_node + .self$size <- .self$size - 1 + cat("Deleted", deleted_data, "at position", position, ". List size:", .self$size, "\n") + return(deleted_data) + }, + + # Delete by value (first occurrence) + delete_by_value = function(value) { + "Remove first occurrence of specified value" + if (is.null(.self$head)) { + cat("List is empty, nothing to delete\n") + return(FALSE) + } + + # If head contains the value + if (.self$head$data == value) { + .self$delete_from_head() + return(TRUE) + } + + current <- .self$head + while (!is.null(current$next_node)) { + if (current$next_node$data == value) { + current$next_node <- current$next_node$next_node + .self$size <- .self$size - 1 + cat("Deleted first occurrence of", value, ". List size:", .self$size, "\n") + return(TRUE) + } + current <- current$next_node + } + + cat("Value", value, "not found in list\n") + return(FALSE) + }, + + # Search for an element + search = function(value) { + "Search for a value and return its position (0-indexed), -1 if not found" + current <- .self$head + position <- 0 + + while (!is.null(current)) { + if (current$data == value) { + cat("Found", value, "at position", position, "\n") + return(position) + } + current <- current$next_node + position <- position + 1 + } + + cat("Value", value, "not found in list\n") + return(-1) + }, + + # Get element at specified position + get = function(position) { + "Get element at specified position without removing it" + if (position < 0 || position >= .self$size) { + stop("Position out of bounds. Valid range: 0 to ", .self$size - 1) + } + + current <- .self$head + for (i in 1:position) { + current <- current$next_node + } + + return(current$data) + }, + + # Get the size of the list + get_size = function() { + "Return the number of elements in the list" + return(.self$size) + }, + + # Check if the list is empty + is_empty = function() { + "Check if the list is empty" + return(.self$size == 0) + }, + + # Convert list to R vector + to_vector = function() { + "Convert linked list to R vector" + if (.self$is_empty()) { + return(c()) + } + + result <- c() + current <- .self$head + while (!is.null(current)) { + result <- c(result, current$data) + current <- current$next_node + } + return(result) + }, + + # Reverse the linked list + reverse = function() { + "Reverse the linked list in place" + if (is.null(.self$head) || is.null(.self$head$next_node)) { + return() # Empty or single element list + } + + prev_node <- NULL + current <- .self$head + + while (!is.null(current)) { + next_temp <- current$next_node + current$next_node <- prev_node + prev_node <- current + current <- next_temp + } + + .self$head <- prev_node + cat("List reversed successfully\n") + }, + + # Find the middle element (useful for algorithms) + find_middle = function() { + "Find the middle element using two-pointer technique" + if (is.null(.self$head)) { + return(NULL) + } + + slow_ptr <- .self$head + fast_ptr <- .self$head + + while (!is.null(fast_ptr$next_node) && !is.null(fast_ptr$next_node$next_node)) { + slow_ptr <- slow_ptr$next_node + fast_ptr <- fast_ptr$next_node$next_node + } + + cat("Middle element:", slow_ptr$data, "\n") + return(slow_ptr$data) + }, + + # Detect cycle in the list (Floyd's algorithm) + has_cycle = function() { + "Detect if there's a cycle in the list using Floyd's algorithm" + if (is.null(.self$head) || is.null(.self$head$next_node)) { + return(FALSE) + } + + slow_ptr <- .self$head + fast_ptr <- .self$head + + while (!is.null(fast_ptr) && !is.null(fast_ptr$next_node)) { + slow_ptr <- slow_ptr$next_node + fast_ptr <- fast_ptr$next_node$next_node + + if (identical(slow_ptr, fast_ptr)) { + return(TRUE) + } + } + + return(FALSE) + }, + + # Print the entire list + print_list = function() { + "Print all elements in the list" + if (.self$is_empty()) { + cat("List is empty\n") + return() + } + + cat("Linked List: ") + current <- .self$head + elements <- c() + + while (!is.null(current)) { + elements <- c(elements, current$data) + current <- current$next_node + } + + cat(paste(elements, collapse = " -> "), "-> NULL\n") + cat("Size:", .self$size, "\n") + }, + + # Clear the entire list + clear = function() { + "Remove all elements from the list" + .self$head <- NULL + .self$size <- 0 + cat("List cleared\n") + } + ) +) + +# ============================================================================== +# EXAMPLES AND DEMONSTRATIONS +# ============================================================================== + +demonstrate_singly_linked_list <- function() { + cat("=================================================================\n") + cat("SINGLY LINKED LIST - COMPREHENSIVE DEMONSTRATION\n") + cat("=================================================================\n\n") + + # Create a new linked list + cat("1. CREATING A NEW SINGLY LINKED LIST\n") + cat("-----------------------------------------------------------------\n") + my_list <- SinglyLinkedList$new() + cat("Created empty linked list\n") + my_list$print_list() + + cat("\n2. INSERTION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Insert at head + cat("Inserting elements at head: 10, 20, 30\n") + my_list$insert_at_head(10) + my_list$insert_at_head(20) + my_list$insert_at_head(30) + my_list$print_list() + + # Insert at tail + cat("\nInserting elements at tail: 5, 1\n") + my_list$insert_at_tail(5) + my_list$insert_at_tail(1) + my_list$print_list() + + # Insert at specific position + cat("\nInserting 15 at position 2\n") + my_list$insert_at_position(15, 2) + my_list$print_list() + + cat("\n3. ACCESS AND SEARCH OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Search for elements + cat("Searching for elements:\n") + my_list$search(15) + my_list$search(99) + + # Get elements by position + cat("\nAccessing elements by position:\n") + cat("Element at position 0:", my_list$get(0), "\n") + cat("Element at position 3:", my_list$get(3), "\n") + + # Find middle element + cat("\nFinding middle element:\n") + my_list$find_middle() + + cat("\n4. DELETION OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Delete from head + cat("Deleting from head:\n") + deleted <- my_list$delete_from_head() + cat("Deleted value:", deleted, "\n") + my_list$print_list() + + # Delete from tail + cat("\nDeleting from tail:\n") + deleted <- my_list$delete_from_tail() + cat("Deleted value:", deleted, "\n") + my_list$print_list() + + # Delete by value + cat("\nDeleting by value (15):\n") + my_list$delete_by_value(15) + my_list$print_list() + + # Delete at position + cat("\nDeleting at position 1:\n") + deleted <- my_list$delete_at_position(1) + my_list$print_list() + + cat("\n5. UTILITY OPERATIONS\n") + cat("-----------------------------------------------------------------\n") + + # Convert to vector + cat("Converting to R vector:\n") + vector_form <- my_list$to_vector() + cat("Vector:", vector_form, "\n") + + # Add more elements for reversal demonstration + cat("\nAdding more elements for reversal demo:\n") + my_list$insert_at_tail(100) + my_list$insert_at_tail(200) + my_list$insert_at_tail(300) + my_list$print_list() + + # Reverse the list + cat("\nReversing the list:\n") + my_list$reverse() + my_list$print_list() + + # Check cycle detection + cat("\nChecking for cycles:\n") + has_cycle <- my_list$has_cycle() + cat("Has cycle:", has_cycle, "\n") + + cat("\n6. PRACTICAL EXAMPLE: STUDENT GRADES\n") + cat("-----------------------------------------------------------------\n") + + # Create a list for student grades + grades_list <- SinglyLinkedList$new() + + cat("Managing student grades using linked list:\n") + grades <- c(85, 92, 78, 96, 83, 89) + for (grade in grades) { + grades_list$insert_at_tail(grade) + } + + cat("Initial grades: ") + grades_list$print_list() + + # Add a new grade at the beginning (latest grade) + cat("\nAdding new grade (95) at the beginning:\n") + grades_list$insert_at_head(95) + grades_list$print_list() + + # Remove the lowest grade (assuming we know it's 78) + cat("\nRemoving grade 78:\n") + grades_list$delete_by_value(78) + grades_list$print_list() + + # Find middle grade + cat("\nMiddle grade in the list:\n") + middle_grade <- grades_list$find_middle() + cat("Middle grade value:", middle_grade, "\n") + + # Convert to vector for statistical analysis + grade_vector <- grades_list$to_vector() + cat("Final grades vector:", grade_vector, "\n") + cat("Average grade:", mean(grade_vector), "\n") + + cat("\n=================================================================\n") + cat("END OF SINGLY LINKED LIST DEMONSTRATION\n") + cat("=================================================================\n") +} + +# Helper function to create a linked list from vector +create_list_from_vector <- function(vector_data) { + "Create a linked list from an R vector" + new_list <- SinglyLinkedList$new() + for (element in vector_data) { + new_list$insert_at_tail(element) + } + return(new_list) +} + +# Examples are available but not run automatically to avoid side effects +# To run examples, execute: demonstrate_singly_linked_list() +if (interactive()) { + cat("Loading Singly Linked List implementation...\n") + cat("Run 'demonstrate_singly_linked_list()' to see comprehensive examples.\n") + cat("Use 'SinglyLinkedList$new()' to create a new linked list.\n") +} + +# Uncomment the following line to run examples automatically: +# demonstrate_singly_linked_list() \ No newline at end of file