Implement a function to find the nth to last node in a linked list
Finding the nth to last node in a linked list is a common interview question that tests your understanding of linked list traversal and pointer manipulation. This post will walk you through different approaches to solving this problem in JavaScript, complete with code examples.
Let’s define a simple linked list node:
class Node {
constructor(data) {
this.data = data;
this.next = null;
} }
Approach 1: Two-Pointer Technique
This approach is arguably the most efficient, utilizing two pointers: a fast
pointer and a slow
pointer. The fast
pointer moves n
nodes ahead of the slow
pointer initially. Then, both pointers move forward simultaneously until the fast
pointer reaches the end of the list. At this point, the slow
pointer will be positioned at the nth to last node.
function nthToLastNode(head, n) {
if (head === null || n <= 0) {
return null;
}
let slow = head;
let fast = head;
// Move fast pointer n nodes ahead
for (let i = 0; i < n; i++) {
if (fast === null) {
return null; // n is larger than the list length
}= fast.next;
fast
}
// Move both pointers until fast reaches the end
while (fast !== null) {
= slow.next;
slow = fast.next;
fast
}
return slow;
}
// Example usage:
let head = new Node(1);
.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
head
let nthNode = nthToLastNode(head, 2); //Find the 2nd to last node
console.log(nthNode.data); // Output: 4
= nthToLastNode(head, 6); //Trying to find a node beyond the list length
nthNode console.log(nthNode); // Output: null
Approach 2: Single Pass with Length Calculation (Less Efficient)
This approach first traverses the list to determine its length. Then, it performs a second traversal to find the nth to last node. While straightforward, it’s less efficient than the two-pointer technique because it requires two passes through the list.
function nthToLastNodeLength(head, n) {
if (head === null || n <= 0) {
return null;
}
let length = 0;
let current = head;
while (current !== null) {
++;
length= current.next;
current
}
if (n > length) {
return null;
}
= head;
current for (let i = 0; i < length - n; i++) {
= current.next;
current
}
return current;
}
//Example Usage (same as above, will produce identical output)
let head2 = new Node(1);
.next = new Node(2);
head2.next.next = new Node(3);
head2.next.next.next = new Node(4);
head2.next.next.next.next = new Node(5);
head2
= nthToLastNodeLength(head2, 2); //Find the 2nd to last node
nthNode console.log(nthNode.data); // Output: 4
= nthToLastNodeLength(head2, 6); //Trying to find a node beyond the list length
nthNode console.log(nthNode); // Output: null
The two-pointer technique is generally preferred due to its single-pass nature and improved efficiency, especially for larger linked lists. Remember to handle edge cases like an empty list or n
being larger than the list’s length.