Visual walkthrough — Stack — LIFO semantics, push - pop - peek, array and linked list implementations
We build ONE object across all figures: a row of numbered boxes (an array) and a single red pointer (the top). Red is always the pointer — nothing else is ever red.
Step 1 — Draw the boxes and name them
WHAT. An array is just a fixed row of storage slots laid side by side. We number them starting at : slot , slot , slot , and so on up to slot for an array of size .
WHY number from 0 and not 1? Because the position of a slot is its distance from the start. Slot is zero steps from the front. This matches how memory addresses work and makes the arithmetic below clean.
PICTURE. Below: five empty boxes, their index written underneath. Nothing is stored yet.

Step 2 — Where does top point when the stack is empty?
WHAT. With no elements at all, top cannot point at a real box, because every real box index is or higher. We need a value that means "nothing here yet". We choose:
Here is a sentinel: a made-up value chosen precisely because it is one step below the first real index .
WHY specifically? Watch what the first push will need. The very first element must land in box . If pushing always does "move up by one, then store", then starting one below — at — puts the first element exactly at . The sentinel is chosen so the same push rule works even for the first element. No special case.
PICTURE. The red pointer sits to the left of box , hanging in empty space at position .

Step 3 — The push rule, born from one question
WHAT. To push(x) we ask: which box is the next free one? The highest filled box is top, so the next free box is . We do two things in this exact order:
- — the arrow means "becomes". We raise the pointer by one so it now names the box that was free.
- — we write the value into the box the pointer now names.
WHY increment FIRST, store SECOND? Because top must always name a filled box. If we stored first at top+1 and then moved, there'd be a moment where top names an empty box — the invariant breaks. Climb, then place.
PICTURE. push(7) on the empty stack: red pointer climbs from to , then appears inside box .

Step 4 — Push again, and watch the pile grow upward
WHAT. Now push(4) then push(9). Each time: climb the red pointer one box, drop the value in.
WHY does this stay ? Notice we never touch boxes or when pushing . We only ever look at the single box the pointer names. No shifting, no scanning — one step of work regardless of how tall the stack is.
PICTURE. Boxes hold . Red pointer sits at box . The "top" is — the last one in.

Recall Why is
the top and not ? Because was pushed last, and the red pointer always tracks the most recently placed box. Last-In sits highest ::: that is LIFO made physical.
Step 5 — The pop rule: read first, shrink second
WHAT. To pop() we return the top value and forget its box:
- — grab the value the red pointer names while it still names it.
- — drop the pointer one box, so the box we just read is now "below the top" and will be overwritten by the next push.
WHY read BEFORE decrement? This is the mirror image of push. If we decremented first, top would name box (holding ), and we'd return — the wrong element — while the real top leaks away untouched. Order = (read, shrink).
PICTURE. pop() on : value is read out (arrow leaving box ), then red pointer slides down to box . The is now "forgotten" (greyed) even though the bytes may linger.

Step 6 — Edge case: popping down to empty
WHAT. Keep popping. pop() returns (top: ), then pop() returns (top: ). Now top is back at .
WHY does reappear? The pop rule subtracts one each time. Starting from the last real element in box , one more decrement lands on — the very sentinel we defined in Step 2. The empty-state and the "just emptied" state are literally the same value, so isEmpty() needs no extra bookkeeping.
PICTURE. Pointer walks ; boxes empty one by one until red hangs left of box again — identical to Step 2.

Step 7 — Degenerate cases: empty pop and full push
WHAT. Two ways the naive rules would misbehave, and how the sentinel + capacity guard them:
- Pop when empty (): reading is nonsense (in Python it silently wraps to the last box — a bug). So we test first: if , raise "pop from empty".
- Push when full (fixed size ): the top box is . If already, then is outside the array. So we test: if , raise "stack full" — this is overflow.
WHY these two exact boundaries? The valid box indices are the whole numbers . top is legal only inside this range. Fall one below and you're empty; sit at the top end and there's no room to climb.
PICTURE. Two side-by-side error states: left, red pointer at with a blocked pop-arrow; right, pointer at box (all boxes full) with a blocked push-arrow.

The one-picture summary
Everything collapses to one number line for top and two arrows. Push slides top right (+1, then store). Pop slides top left (read, then −1). The two walls at (empty) and (full) are the only forbidden moves.

Recall Feynman: tell the whole walkthrough to a friend
Picture a row of numbered mail slots and one red flag that points at "the current top slot". When there's nothing, the flag rests just left of slot 0 — we call that position so the very first push works with no special rule. To push, the flag hops one slot to the right and we drop the value where it lands; that's why push is raise then store. To pop, we grab whatever the flag points at first, then let the flag drop one slot to the left — read then shrink, because grabbing after dropping would grab the wrong slot. Keep popping and the flag walks back to , exactly where it began — empty is empty is empty. Only two moves are illegal: dropping the flag below (popping an empty stack) or pushing past the last slot (overflow). That's the entire array stack: one integer, two moves, two walls. Every operation touches exactly one slot, so it's all . See Dynamic Array / Amortized Analysis for how the right wall can be pushed outward when the array is allowed to grow, and Recursion and the Call Stack for the same flag-and-slots picture running your program's function calls.
Recall Related builds
- Linked List — singly linked — the other implementation, where the "top" is the head pointer instead of an integer index.
- Expression Evaluation — infix to postfix and Depth-First Search — two algorithms that are just this push/pop dance in disguise.
- Queue — FIFO semantics — the mirror-image structure where you take from the other end.