GeeksforGeeks Easy

Implement Stack using Arrays

Implement Stack using Arrays: build a stack — push, pop, top, size — on a fixed array.

Constraints
  • 1 <= capacity <= 10⁵
  • Operations: push, pop, top, size
  • Pop or top on an empty stack must be handled explicitly
stackdesign
Open on GeeksforGeeks ↗
02

Intuition

To implement stack using arrays you need LIFO behaviour: the last element pushed is the first one popped. What makes this the easiest of the container-implementation problems is that the newest element is always the one at the end, and the end of an array is the cheapest place to work. So the entire structure is an array plus a single integer. Call it top, holding the index of the last stored element. Push writes at top + 1 and increments; pop reads at top and decrements. Nothing ever shifts, which is precisely why every operation is O(1) — compare this with a queue on an array, where a naive dequeue moves every remaining element. Using −1 for the empty state is the convention worth adopting: - top == -1 means empty, so the first push lands at index 0 with no special case. The part that actually needs care is the boundaries. Popping or peeking an empty stack is underflow, and pushing onto a full fixed array is overflow. Both must be checked before touching the array, because both would otherwise read or write outside the valid range — the kind of bug that corrupts data silently rather than failing loudly.

How to spot this pattern

The foundational exercise: a stack is an array plus one index. Everything interesting is in the boundary conditions — top == -1 means empty, top == capacity - 1 means full. Getting those two right is the whole problem, and they're the same two checks every fixed-capacity structure needs.

03

Approach

Try it first

Before reading on: price up what enumerating every case costs here, then ask what unresolved thing you are holding, and what event finally resolves it. Aim for O(1) per op time and O(cap) space.

1

Track a single index

Store a fixed-capacity array and an integer top, initialised to −1 for empty. That one variable is the complete state of the stack — no head pointer, no count, no wrapping arithmetic.

2

Push at the next slot

Increment top, then write the value at a[top]. The new element is the newest and belongs at the end, which is exactly where the array is cheapest to modify. One increment and one write, always O(1).

3

Pop from the current slot

Read a[top], then decrement top. There is no need to erase the old value — it sits above the new top and is unreachable through the stack interface, and the next push will overwrite it.

4

Guard underflow and overflow

Return an error or sentinel when popping or peeking with top == -1, and when pushing with top == capacity - 1. Both checks must come before the array access, since either would otherwise index outside the array and corrupt memory in a language that permits it.

5

Grow the array for a dynamic stack

If the capacity should not be fixed, allocate a larger array on overflow — typically double the size — and copy the elements across. Copying is O(n) but happens rarely enough that pushes stay amortised O(1). This is exactly how Python's list.append and C++'s vector::push_back behave.

04

Solution & live demo

▶1class Stack:
▶2 def __init__(self, cap):
▶3 self.a = [0] * cap
▶4 self.top = -1
▶5 
▶6 def push(self, x):
▶7 if self.top == len(self.a) - 1:
▶8 raise OverflowError
▶9 self.top += 1
▶10 self.a[self.top] = x
▶11 
▶12 def pop(self):
▶13 if self.top == -1:
▶14 raise IndexError("empty")
▶15 v = self.a[self.top]
▶16 self.top -= 1
▶17 return v
▶18 
▶19 def peek(self):
▶20 return self.a[self.top] if self.top >= 0 else None
05

Common pitfalls

Incrementing the index after writing

✗ Wrong
self.a[self.top] = x
self.top += 1
✓ Right
self.top += 1
self.a[self.top] = x

With top starting at −1 as the empty marker, writing first targets index −1, which in Python silently overwrites the last slot of the array. Advance to the new position, then write to it.

Checking overflow against the wrong bound

✗ Wrong
if self.top == len(self.a): raise OverflowError
✓ Right
if self.top == len(self.a) - 1: raise OverflowError

top is the index of the last stored item, so a full array has top == capacity - 1. Comparing against capacity lets one extra push run off the end.

Returning a value after decrementing

✗ Wrong
self.top -= 1
return self.a[self.top]
✓ Right
v = self.a[self.top]
self.top -= 1
return v

Decrementing first returns the element below the top — the one that should survive the pop. Read the value while the index still points at it.

06

Edge cases

Pop from empty stack

Explicit underflow check; raise or return sentinel.

Interleaved push/pop at capacity

top bounces at the boundary; indices never leak past it.

07

Complexity

Time
O(1) per op
Space
O(cap)
No shifting, ever.