Skip to content

Commit d04ded1

Browse files
feat: add Two-Pointer Technique for linked lists (#283)
1 parent f4b992a commit d04ded1

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
* [Circular Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/circular_linked_list.r)
7070
* [Doubly Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/doubly_linked_list.r)
7171
* [Singly Linked List](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/singly_linked_list.r)
72+
* [Two-Pointer Technique (Fast and Slow Pointers)](https://github.com/TheAlgorithms/R/blob/HEAD/linked_list_algorithms/two_pointer_technique.r)
7273

7374
## Machine Learning
7475
* [Gradient Boosting](https://github.com/TheAlgorithms/R/blob/HEAD/machine_learning/gradient_boosting.r)
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Two-Pointer Technique (Fast and Slow Pointers / Tortoise and Hare)
2+
#
3+
# Template and practical application for singly linked lists using the
4+
# fast & slow pointer technique. This file provides:
5+
# - A minimal Node reference-class local to this file
6+
# - Generic helper: build list from a vector
7+
# - detect_cycle(head): returns TRUE/FALSE using tortoise & hare
8+
# - find_middle(head): returns the middle node's data (for even length returns the second middle)
9+
# - nth_from_end(head, n): returns the data of the Nth node from the end (1-based)
10+
#
11+
# Practical application demonstrated: finding the Nth node from the end.
12+
13+
TwoPtrNode <- setRefClass("TwoPtrNode",
14+
fields = list(
15+
data = "ANY",
16+
next_node = "ANY"
17+
),
18+
methods = list(
19+
initialize = function(data = NULL, next_node = NULL) {
20+
.self$data <- data
21+
.self$next_node <- next_node
22+
},
23+
24+
print = function() {
25+
cat("Node(data =", .self$data, ")\n")
26+
}
27+
)
28+
)
29+
30+
# Build a singly linked list from an R vector of values. Returns the head node.
31+
build_list_from_vector <- function(vec) {
32+
if (length(vec) == 0) return(NULL)
33+
head <- TwoPtrNode$new(data = vec[1])
34+
current <- head
35+
if (length(vec) > 1) {
36+
for (v in vec[-1]) {
37+
new_node <- TwoPtrNode$new(data = v)
38+
current$next_node <- new_node
39+
current <- new_node
40+
}
41+
}
42+
return(head)
43+
}
44+
45+
# Convert linked list to vector for easy printing/inspection
46+
# This is defensive against cycles by truncating after `max_nodes` items.
47+
list_to_vector <- function(head, max_nodes = 1000) {
48+
out <- c()
49+
cur <- head
50+
i <- 0
51+
while (!is.null(cur) && i < max_nodes) {
52+
out <- c(out, cur$data)
53+
cur <- cur$next_node
54+
i <- i + 1
55+
}
56+
if (!is.null(cur)) {
57+
out <- c(out, "... (truncated or cycle detected)")
58+
}
59+
return(out)
60+
}
61+
62+
# Detect cycle using fast and slow pointers (Tortoise & Hare)
63+
detect_cycle <- function(head) {
64+
if (is.null(head)) return(FALSE)
65+
slow <- head
66+
fast <- head
67+
while (!is.null(fast) && !is.null(fast$next_node)) {
68+
slow <- slow$next_node
69+
fast <- fast$next_node$next_node
70+
if (identical(slow, fast)) return(TRUE)
71+
}
72+
return(FALSE)
73+
}
74+
75+
# Find middle node using two pointers. For even-length lists this returns
76+
# the second middle (i.e., for 1->2->3->4 it returns 3).
77+
find_middle <- function(head) {
78+
if (is.null(head)) return(NULL)
79+
slow <- head
80+
fast <- head
81+
while (!is.null(fast) && !is.null(fast$next_node)) {
82+
slow <- slow$next_node
83+
fast <- fast$next_node$next_node
84+
}
85+
return(slow)
86+
}
87+
88+
# Return the data of the Nth node from the end (1-based). Throws an error
89+
# if n is invalid or greater than list length.
90+
nth_from_end <- function(head, n) {
91+
if (is.null(head)) stop("List is empty")
92+
if (n <= 0) stop("n must be a positive integer")
93+
94+
fast <- head
95+
# Advance fast by n steps
96+
for (i in seq_len(n)) {
97+
if (is.null(fast)) stop(sprintf("n (%d) is larger than the list length", n))
98+
fast <- fast$next_node
99+
}
100+
101+
slow <- head
102+
# Move both until fast is NULL; slow will be at the Nth from end
103+
while (!is.null(fast)) {
104+
slow <- slow$next_node
105+
fast <- fast$next_node
106+
}
107+
return(slow$data)
108+
}
109+
110+
# ---- Practical demonstration ----
111+
if (sys.nframe() == 0) {
112+
cat("Two-Pointer Technique demo\n")
113+
head <- build_list_from_vector(1:7)
114+
cat("List:", paste(list_to_vector(head), collapse = " -> "), "\n")
115+
116+
mid <- find_middle(head)
117+
cat("Middle node data:", ifelse(is.null(mid), "NULL", mid$data), "\n")
118+
119+
n <- 2
120+
nth <- nth_from_end(head, n)
121+
cat(sprintf("%d-th node from the end: %s\n", n, nth))
122+
123+
cat("Cycle detected?", detect_cycle(head), "\n")
124+
125+
# Create a cycle for testing (connect tail to node with data=3)
126+
tail <- head
127+
while (!is.null(tail$next_node)) tail <- tail$next_node
128+
cur <- head
129+
while (!is.null(cur) && cur$data != 3) cur <- cur$next_node
130+
if (!is.null(cur)) tail$next_node <- cur
131+
cat("After creating a cycle (tail -> node with data 3):\n")
132+
cat("Cycle detected?", detect_cycle(head), "\n")
133+
}

0 commit comments

Comments
 (0)