Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Data Structures, Lightly  ›  Lesson

Arrays & Linked Lists

Arrays and Linked Lists 14 minPython's list as a dynamic array, and a hand-built linked list — the same job, two different shapes
Your task
Write round_trip(values). Build a linked list from values using a Node class (val, next), then walk it back into a plain Python list and return that. Example: round_trip([1, 2, 3]) -> [1, 2, 3].

Arrays & Linked Lists

In short: a Python list is a dynamic array — one contiguous block of memory, so values[i] jumps straight there and append is usually instant. A linked list is a chain of separate Node objects, each holding a value and a pointer to the next — cheap to splice a node in or out once you're standing at it, but no jumping to index i: you have to walk there one link at a time. Python rarely asks you to build a linked list yourself (list and collections.deque already cover almost every real case), but the shape shows up constantly in interviews, and understanding it makes "why is this slow?" answerable. The DSA course covers the Big-O of both in full; this is the Python-shaped tour.

Top row: five numbered boxes side by side labelled 0,1,2,3,4, each reachable directly — "array: jump straight to index i". Below: five separate boxes connected by arrows, each pointing only to the next — "linked list: walk one link at a time to get anywhere"

Prepend a song to a playlist

A playlist app keeps the queue as a plain Python list — values[i] reaches any track instantly, which is exactly what a dynamic array is built for. The trouble starts the moment someone taps "play next" on a track that isn't at the front yet. Pushing it to position 0 feels like it should cost the same as adding a track at the end, but it doesn't:

What does this print?
values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
values.insert(0, 1)
print(values[5])

That's the mistake: treating insert(0, x) as a cheap operation because values[i] is cheap. They are not the same thing. Indexing jumps straight to a slot; inserting at the front has to physically move every item after it one slot over to make room, which gets slower the longer the playlist gets. append stays cheap no matter how long the list grows, because nothing after it needs to move — there's nothing after it yet. That asymmetry (cheap at the end, expensive at the front) is the whole reason collections.deque exists: it's built to make both ends cheap, which you'll meet properly in the next lesson.

Walk a browser's back-button history

A browser can't jump to "the 7th page I visited" the way a list jumps to an index — it only knows "the page before this one." That's a linked list: a chain of separate Node objects, each holding a value and a pointer to whatever comes next, with None marking the end of the chain:

python

There's no history[2] — walking one .next at a time is the only way in. The mistake people make here is reaching for square brackets out of habit, because that's how every sequence has worked so far in Python:

broken — fix itTypeError

Run it and read the error. Then print the third node's value WITHOUT square brackets.

A Node has no __getitem__, so indexing simply isn't defined — Python raises TypeError rather than guessing. The payoff for giving up indexing: once you're already standing at a node (say, the "product" page), splicing a new page in next to it is a couple of pointer rewrites — no shifting anything else in the whole chain, which is exactly the opposite trade-off from the playlist above.

Picking between the two

In real Python code, that choice almost always comes down to one question: do you need to jump to a position by index, or only ever walk forward from wherever you are? A playlist, a spreadsheet row, anything you address by position — use a list. A browser's back/forward history, an undo chain, anything you only ever step through one link at a time and need to splice mid-walk — a linked chain (or, in practice, collections.deque, which you meet properly in the next lesson) fits better. Python's own list quietly covers both jobs reasonably well for small data, which is exactly why this trade-off only starts to matter once a collection gets large or the front gets touched constantly.

Quick practice

What does this print?
class Node:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

a = Node(1)
b = Node(2)
a.next = b
b.next = a
print(a.next.next.val)
What does this print?
values = [1, 2, 3, 4, 5]
print(values[2], values[-1])

🎯 Your turn

Write round_trip(values). Build a linked list from values using a Node class, then walk it back into a plain Python list and return that:

  • round_trip([1, 2, 3]) → [1, 2, 3]
  • round_trip([]) → []

Hint — build(values) links each new Node onto a running tail; to_list(head) walks node = node.next until it hits None, collecting .val as it goes.

Press ▶ Run, try the Live App chips, then ✓ Check. After that, the practice set below reverses a chain in place — no library, just pointers.

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upInsert at the front of an array

Write prepend(values, x) that inserts x at the very front of values and returns it.

arraysarray
DrillA two-node chain

Using a Node class with val and next, write two_node_values(a, b) that builds a two-node chain (a then b) and returns [head.val, head.next.val].

linked-listsnode
BuildSum a chain by walking it

Write chain_sum(values). Build a linked list from values, then walk it with a loop (not sum(values) directly) and return the total.

linked-lists
BossReverse a chain in place

Write reverse_chain(values). Build a linked list from values, reverse its pointers IN PLACE (the classic prev/curr walk — no list, no slicing), then return the reversed chain as a list.

linked-listsreverse-list
CapstoneFind the middle, fast and slow

Write middle_value(values). Build a linked list from values, then find the middle node's value using the fast/slow pointer technique: slow moves one step at a time, fast moves two, and when fast runs out, slow is at the middle. Return None for an empty list.

linked-listsfast-slow-pointers
round_trip.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
round_trip(values) → listBuild a linked list from values, then walk it back into a list.
try:
returned
No app map for this section yet.
Press Run to execute your code.
Press Check to run the tests for this lesson.