π‘ Common Mistake:is_full is abstract β fullness depends on storage capacity, which only a concrete subclass knows; is_empty reads shared length so it is concrete.
πΉ ArrayStack
ArrayStack(Stack[T])
class ArrayStack(Stack[T]): MIN_CAPACITY = 1 # ArrayR can't be size 0 def __init__(self, max_capacity: int) -> None: Stack.__init__(self) # length = 0 self.array = ArrayR(max(self.MIN_CAPACITY, max_capacity)) def is_full(self) -> bool: return len(self) == len(self.array) def push(self, item: T) -> None: if self.is_full(): raise Exception("Stack is full") self.array[len(self)] = item; self.length += 1 # write then top++ def pop(self) -> T: if self.is_empty(): raise Exception("Stack is empty") self.length -= 1; return self.array[self.length] # top-- then return def peek(self) -> T: if self.is_empty(): raise Exception("Stack is empty") return self.array[self.length - 1]
π‘ Common Mistake:Growable push is amortised β doubling pays the O(n) copy via geometric series (<3n); constant growth β Ξ(n2). Guard with exceptions, not assert (-O strips it).
πΉ LinkStack
LinkStack(Stack[T])
class LinkStack(Stack[T]): def __init__(self): Stack.__init__(self); self.top = None def is_full(self): return False # linked -> never full def push(self, item): new = Node(item); new.link = self.top; self.top = new; self.length += 1 def pop(self): if self.is_empty(): raise ValueError("Stack is empty") item = self.top.item; self.top = self.top.link; self.length -= 1 # old node GC'd return item
π‘ Common Mistake:Reassign top before unreachable β pop must set top=top.link so GC reclaims the old node; cost is one pointer/element + poor locality.
βοΈ Core Decision Matrix
Variant / Strategy
Trigger Condition
Advantage (Pro)
Disadvantage (Con) / Complexity Bound
Cache / Memory Impact
ArrayStack (fixed)
Capacity known, dense
O(1) push/pop/peek
raises if full; wastes (Cβn)w slack
contiguous, cache-friendly
ArrayStack (growable)
Unknown but bounded
O(1) amortised push
resize copy O(n) worst
contiguous, doubles on grow
LinkStack
Variable / unbounded
true O(1), never full
+1 pointer/node; no random access
scattered, poor locality
When It Flips: array of capacity C holding n uses βCw; LinkStack uses β2nw β LinkStack wins when n<C/2 (array under half-full), ArrayStack wins when nearly full.
π Exam Execution Trace
Manual Execution Trace
ArrayStack(3): push 7, push 4, pop, push 9, peek
Step / State
Trigger Op
array (cap 3)
length
Top idx
Return Payload
0 (Init)
init
[_, _, _]
0
β
β
1
push 7
[7, _, _]
1
0
β
2
push 4
[7, 4, _]
2
1
β
3
pop
[7, 4, _]
1
0
4
4
push 9
[7, 9, _]
2
1
β
5
peek
[7, 9, _]
2
1
9
Applied Exercise
Problem: Bound the cost of npushes on a doubling ArrayStack.
Derivation Proof / Hand-Calculation Walkthrough: