Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 7

Singly Linked List: Structure and Traversal

Recommended resources

Hello! Welcome to your next lesson.

In our previous session, we did a deep dive into dynamic arrays. We implemented one from scratch and analyzed its performance, uncovering a key takeaway: while dynamic arrays offer fast access and amortized appends, they are inefficient for insertions and deletions in the middle, costing time due to the need to shift elements. This is a common bottleneck that interviewers love to probe.

Today, we'll tackle that exact problem by exploring the singly linked list. Your learning goal is to implement the structure of a singly linked list and perform basic traversal. This data structure trades fast index-based access for efficient insertions and deletions. Mastering its structure and traversal is the first and most critical step in solving a vast category of interview problems.

What is a Linked List?

At its core, a linked list is a linear data structure, but unlike an array, its elements are not stored in contiguous memory locations. Instead, elements are stored in objects called nodes. Each node contains two pieces of information:

  1. The actual data or value.
  2. A reference (or pointer) to the next node in the sequence.

The list is accessed through a single reference to the first node, called the head. The last node's next pointer points to None (or null), signaling the end of the list.

Let's watch a short video that visually explains this concept and contrasts it with the arrays we've just studied.

Linked Lists for Technical Interviews - Full Course

The video 'Linked Lists for Technical Interviews' from freeCodeCamp.org provides a clear, visual introduction to the core concepts.

Please watch from 01:08 to 04:25. This segment introduces nodes, 'next' pointers, and the key terms 'head' and 'tail'. Notice how it sets up the contrast with arrays' contiguous memory.


Implementing the Structure in Python

To build a linked list, we need to model its structure in code. This is typically done using two classes: a Node class to represent each element and a LinkedList class to manage the overall structure. Your experience with Object-Oriented Programming will be very helpful here.

1. The Node Class

The Node is the fundamental building block. It's a simple container for the value and the pointer to the next node.

class Node:
    """
    An object for storing a single node of a linked list.
    Models two attributes: value and the link to the next node.
    """
    def __init__(self, value):
        self.value = value  # The data stored in the node
        self.next = None    # The reference to the next node

2. The LinkedList Class

The LinkedList class acts as a wrapper for the entire sequence of nodes. For now, its only job is to hold a reference to the head of the list. An empty list is one where head is None.

class LinkedList:
    """
    An object for the linked list.
    Models one attribute: the head of the list.
    """
    def __init__(self):
        self.head = None # Initially, the list is empty

This structure is visualized well in the following image, which shows how individual Node objects are instantiated and then linked together by setting their next attributes.

Building a Singly Linked List with Classes
This diagram illustrates how a singly linked list is constructed. On the left, you see the Python-like code to create `Node` objects and link them. On the right, you see the resulting structure in memory, with each node pointing to the next, starting from the `head`.

The Core Algorithm: Traversal

How do we visit every element in a linked list? Since we can't use an index like in an array, we must perform a traversal. This is the most fundamental linked list operation, forming the basis for searching, printing, inserting, and deleting.

The algorithm is straightforward:

  1. Create a temporary pointer, let's call it current, and initialize it with the head of the list.
  2. Start a loop that continues as long as current is not None.
  3. Inside the loop, "visit" the node (e.g., print its value).
  4. Move to the next node by updating the pointer: current = current.next.
Traversal in Singly Linked List
This diagram shows the process of traversal. Starting from the `head`, we follow the `next` pointer of each node until we reach `NULL` (or `None`), which signifies the end of the list.

The following video provides an exceptionally detailed walkthrough of implementing traversal, both iteratively (with a loop) and recursively. Understanding both is crucial for interviews.

Linked Lists for Technical Interviews - Full Course

Let's now watch a detailed explanation of traversal. Pay close attention to the logic, especially the loop condition and the base case for the recursion.

Watch from 09:18 to 23:34. The first part (until 19:44) covers the iterative method, which is the most common and what you should master first. The second part covers the recursive approach. Notice how the logic traces line-by-line in the video.


Putting it all Together

Now, let's integrate the iterative traversal logic into our LinkedList class as a print_list method. This makes our class more useful and provides a concrete example of traversal.

class LinkedList:
    def __init__(self):
        self.head = None

    # We will add insertion methods here in the next lesson
    
    def print_list(self):
        """
        Traverses the linked list and prints the values of the nodes.
        """
        current_node = self.head
        values = []
        while current_node:
            values.append(str(current_node.value))
            current_node = current_node.next
        
        if not values:
            print("List is empty")
        else:
            print(" -> ".join(values))

# Let's test it out
my_list = LinkedList()

# Manually create nodes and link them for now
node1 = Node(10)
node2 = Node(20)
node3 = Node(30)

my_list.head = node1
node1.next = node2
node2.next = node3

my_list.print_list()  # Expected output: 10 -> 20 -> 30

The time complexity of traversal is because we must visit each of the nodes once. The space complexity is for an iterative traversal since we only use a single extra pointer (current_node). For a recursive traversal, the space complexity becomes due to the call stack depth.

Test your understanding!

Using the Node and LinkedList classes we've defined, add a method to the LinkedList class called get_length() that traverses the list and returns the number of nodes it contains.

class LinkedList:
    # ... __init__ and print_list from above ...

    def get_length(self):
        # Your implementation here
        pass
Show answer

The logic is almost identical to the print_list method, but instead of appending to a list of values, you increment a counter.

def get_length(self):
    """
    Traverses the linked list and returns its length.
    """
    count = 0
    current_node = self.head
    while current_node:
        count += 1
        current_node = current_node.next
    return count

# --- Test ---
# Using the same my_list from before
print(my_list.get_length()) # Expected output: 3

Conclusion

In this lesson, you've established the foundation for working with linked lists. You now understand their core structure and how they differ from arrays.

Key Takeaways:

  • A singly linked list is composed of nodes, where each node contains a value and a next pointer to the subsequent node.
  • The entire list is managed via a single head pointer. The end of the list is marked by a None pointer.
  • Traversal is the fundamental operation for accessing nodes, which involves starting at the head and following the next pointers until you reach None. This is an operation.

We've built the "scaffolding" of our linked list. In the next lesson, we will put it to use by implementing the very operations that make linked lists so powerful: insertion and deletion. You'll see how manipulating a few next pointers allows us to add and remove nodes efficiently.