Skip to Content
Course content

121: Whiteboard Practice: Implementing a Linked List in Ruby

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I remember a junior developer I mentored a few years back who completely froze during a technical interview at a high-frequency trading firm. The interviewer asked him to build a basic undo-redo buffer. He instinctively started with a Ruby Array, but the interviewer stopped him immediately, pointing out that shifting elements from the front of a massive array is a performance killer—$O(n)$ time complexity for every single operation. That's when the "Linked List" conversation started. He knew the theory from a textbook, but translating "nodes and pointers" into actual Ruby code on a whiteboard is a completely different beast. That's why we're doing this; it's not about replacing Ruby's powerful Array class in your daily work, but about understanding how to manage memory and references manually when performance constraints demand it.

Building the Node Foundation

At its core, a linked list isn't a single object, but a collection of independent objects called nodes that point to one another. Think of it like a scavenger hunt: you don't have a map of every location, you just have the first clue, and that clue tells you where to find the next one.

In Ruby, we represent this with a simple class. Each node needs two things: the actual data it's holding and a reference to the next node in the sequence. If there is no next node, we just set it to nil.

class Node
  attr_accessor :value, :next_node

  def initialize(value)
    @value = value
    @next_node = nil
  end
end

I prefer using attr_accessor here because, during a whiteboard session, you want to keep your boilerplate to a minimum. It gives you the getters and setters you need to rewire the list without writing ten different methods.

Managing the Chain

Now that we have a node, we need a way to manage them. The LinkedList class itself doesn't actually hold a list of items; it only holds a reference to the head (the first node). From the head, you can reach any other element in the list by following the next_node references.

Adding an element to the front (prepending) is incredibly efficient—$O(1)$—because you just create a new node and tell it to point to the current head. Appending to the end, however, requires you to "walk" the entire list until you find the node that points to nil.

class LinkedList
  attr_reader :head

  def initialize
    @head = nil
  end

  def prepend(value)
    new_node = Node.new(value)
    new_node.next_node = @head
    @head = new_node
  end

  def append(value)
    return prepend(value) if @head.nil?

    current = @head
    current = current.next_node while current.next_node

    current.next_node = Node.new(value)
  end
end

Notice the while loop in the append method. This is the bread and butter of linked list manipulation. You're essentially surfing through the objects until you hit the end of the line.

Walking the List and Extracting Data

Since you can't access linked list elements by index like you do with array[4], you have to implement your own traversal. Whether you're printing the list or searching for a specific value, the pattern is always the same: start at the head and move to the next_node until you hit nil.

I often see developers try to use recursion for this, which is elegant, but in a production environment (or a strict interview), an iterative approach is safer to avoid stack overflow errors on very long lists.

def print_list
  current = @head
  while current
    print "#{current.value} -> "
    current = current.next_node
  end
  puts "nil"
end

When you're practicing this on a whiteboard, the most common mistake is forgetting to update the current variable inside your loop. If you forget current = current.next_node, you've just created an infinite loop that will crash your program. Always double-check your pointer updates.




📋 Practical Task

Exercise: Implementing a "Remove by Value" Method for a Singly Linked List

In this exercise, you will extend the LinkedList class implemented in the lesson. Your goal is to create a method called remove(value) that searches for the first occurrence of a specific value and removes that node from the list by "stitching" the previous node directly to the next one.

Requirements:

  • If the head contains the value, the head should be updated to the second node.
  • If the value is in the middle or end of the list, the node preceding the target must have its next_node updated to skip over the target node.
  • If the value is not found, the list should remain unchanged.
  • The method should return true if an element was removed, and false otherwise.
# Starter Code
class Node
  attr_accessor :value, :next_node
  def initialize(value)
    @value = value
    @next_node = nil
  end
end

class LinkedList
  attr_reader :head

  def initialize
    @head = nil
  end

  def append(value)
    return prepend(value) if @head.nil?
    current = @head
    current = current.next_node while current.next_node
    current.next_node = Node.new(value)
  end

  def prepend(value)
    new_node = Node.new(value)
    new_node.next_node = @head
    @head = new_node
  end

  # TODO: Implement the remove(value) method here
end

# Test your implementation:
list = LinkedList.new
list.append("A")
list.append("B")
list.append("C")
# list is A -> B -> C -> nil

list.remove("B") 
# list should now be A -> C -> nil
Rating
0 0

There are no comments for now.

to be the first to leave a comment.