Visualizer CodeViz · Algorithms, visualized

Part I · Stacks and queues · week 2

Stack (resizing array)

Keep items in an array and double it when it fills. Most pushes are one assignment; the rare doubling is paid for by the cheap pushes before it.

Run the animation, step by step → generated live from any input you type — nothing is pre-recorded

Cost and properties

push / popO(1) amortised
worst-case pushO(n) — the doubling
space8n to 32n bytes
ruledouble when full, halve at ¼

Reference: Sedgewick & Wayne, §1.3.

Stack (resizing array) in Python

Runnable as-is, and written for reading. Watch the highlighted line move through it in the animation, or send it straight to the visualizer — it arrives with a test case ready to run, yours to edit.

Open in Visualizer

class ResizingArrayStack:
    def __init__(self):
        self.a = [None]                   # capacity 1
        self.n = 0                        # items in a[0..n-1]

    def _resize(self, capacity):          # this is the expensive part
        self.a = self.a[:self.n] + [None] * (capacity - self.n)

    def push(self, item):
        if self.n == len(self.a):
            self._resize(2 * len(self.a))  # DOUBLE when full
        self.a[self.n] = item
        self.n += 1

    def pop(self):
        if self.n == 0:
            raise IndexError('stack underflow')
        self.n -= 1
        item = self.a[self.n]
        self.a[self.n] = None             # avoid loitering
        if self.n > 0 and self.n == len(self.a) // 4:
            self._resize(len(self.a) // 2)  # HALVE at one quarter full
        return item

Why it works

Amortised, not worst case

Growing from 1 to n costs 1 + 2 + 4 + … + n < 2n copies in total, spread over n pushes: about 3 array accesses per push on average. Any single push can still cost n. The distinction is the point — amortised analysis is a claim about a sequence of operations, never about one.

Why halve at a quarter, not a half

Halving at half-full would thrash: push (double), pop (halve), push (double) … each operation copying the whole array. Shrinking only at one quarter leaves the array between 25% and 100% full, so after a resize you need Θ(n) operations before the next one. Slack is what buys the amortised bound.

Compare the two stacks

  • Linked list: constant worst case, more memory, pointer chasing.
  • Resizing array: less memory, better locality (much faster in practice), occasional slow operation.

Same API. This is the first place the course makes you choose an implementation by which cost you care about, not by which is “better”.

Loitering again

self.a[self.n] = None is not tidiness — without it the array holds a reference to a popped object forever, and the garbage collector cannot reclaim it. A memory leak that no test will catch.

What to try in the animation

A word pushes it; a - pops. The default is the course's tobe.txt test string.

The animation is generated from the input box, in your browser — press Animate after editing it. To execute the Python itself, use Open in Visualizer above.

Open Stack (resizing array) in the player →

The rest of Stacks and queues