Add stack-queue module: ten stack and queue interview problems, monotonic stack, min stack, queue from stacks and stack from a queue, brute-force cross-checks, dry-run traces, operation counts, pitfalls (11 tests)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6nxynun5JFivebVb1
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
Balanced brackets: every step of four inputs
|
||||
============================================
|
||||
|
||||
input: {[()]} firstMismatch = -1
|
||||
read '{' at 0: push '}' waiting for (top first): }
|
||||
read '[' at 1: push ']' waiting for (top first): ] }
|
||||
read '(' at 2: push ')' waiting for (top first): ) ] }
|
||||
read ')' at 3: pop ')', matches waiting for (top first): ] }
|
||||
read ']' at 4: pop ']', matches waiting for (top first): }
|
||||
read '}' at 5: pop '}', matches waiting for (top first): (empty)
|
||||
end of input: waiting for (empty) -> balanced
|
||||
|
||||
input: ([)] firstMismatch = 2
|
||||
read '(' at 0: push ')' waiting for (top first): )
|
||||
read '[' at 1: push ']' waiting for (top first): ] )
|
||||
read ')' at 2: expected ']' -> NOT balanced
|
||||
|
||||
input: ()) firstMismatch = 2
|
||||
read '(' at 0: push ')' waiting for (top first): )
|
||||
read ')' at 1: pop ')', matches waiting for (top first): (empty)
|
||||
read ')' at 2: stack is empty, nothing to close -> NOT balanced
|
||||
|
||||
input: (() firstMismatch = 0
|
||||
read '(' at 0: push ')' waiting for (top first): )
|
||||
read '(' at 1: push ')' waiting for (top first): ) )
|
||||
read ')' at 2: pop ')', matches waiting for (top first): )
|
||||
end of input: waiting for ) -> NOT balanced, openers left over
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Reverse Polish Notation: stack after every token
|
||||
================================================
|
||||
|
||||
tokens: [2, 1, +, 3, *] result = 9
|
||||
token 2: push 2 stack(top first) = [2]
|
||||
token 1: push 1 stack(top first) = [1, 2]
|
||||
token +: pop 1 then 2, compute 2 + 1 = 3 stack(top first) = [3]
|
||||
token 3: push 3 stack(top first) = [3, 3]
|
||||
token *: pop 3 then 3, compute 3 * 3 = 9 stack(top first) = [9]
|
||||
|
||||
tokens: [4, 13, 5, /, +] result = 6
|
||||
token 4: push 4 stack(top first) = [4]
|
||||
token 13: push 13 stack(top first) = [13, 4]
|
||||
token 5: push 5 stack(top first) = [5, 13, 4]
|
||||
token /: pop 5 then 13, compute 13 / 5 = 2 stack(top first) = [2, 4]
|
||||
token +: pop 2 then 4, compute 4 + 2 = 6 stack(top first) = [6]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
Min stack: both internal stacks after every operation
|
||||
=====================================================
|
||||
|
||||
push 5 values=[5] mins=[5] (new or equal minimum, also pushed on mins)
|
||||
push 3 values=[3, 5] mins=[3, 5] (new or equal minimum, also pushed on mins)
|
||||
push 7 values=[7, 3, 5] mins=[3, 5]
|
||||
push 3 values=[3, 7, 3, 5] mins=[3, 3, 5] (new or equal minimum, also pushed on mins)
|
||||
getMin values=[3, 7, 3, 5] mins=[3, 3, 5] getMin() = 3
|
||||
pop -> 3 values=[7, 3, 5] mins=[3, 5] (it was the minimum, mins popped too)
|
||||
getMin values=[7, 3, 5] mins=[3, 5] getMin() = 3
|
||||
pop -> 7 values=[3, 5] mins=[3, 5]
|
||||
getMin values=[3, 5] mins=[3, 5] getMin() = 3
|
||||
pop -> 3 values=[5] mins=[5] (it was the minimum, mins popped too)
|
||||
getMin values=[5] mins=[5] getMin() = 5
|
||||
|
||||
getMin results: [3, 3, 3, 5] (identical for MinStack and EncodedMinStack)
|
||||
@@ -0,0 +1,17 @@
|
||||
Next greater element: the stack of pending indices, step by step
|
||||
================================================================
|
||||
|
||||
input [2, 1, 2, 4, 3]
|
||||
i=0 value=2: push 0 pending(top first) = [0]
|
||||
i=1 value=1: push 1 pending(top first) = [1, 0]
|
||||
i=2 value=2: pop index 1 (value 1) -> its answer is 2; push 2 pending(top first) = [2, 0]
|
||||
i=3 value=4: pop index 2 (value 2) -> its answer is 4; pop index 0 (value 2) -> its answer is 4; push 3 pending(top first) = [3]
|
||||
i=4 value=3: push 4 pending(top first) = [4, 3]
|
||||
indices still pending have no greater element to their right: [4, 3] -> stay -1
|
||||
result [4, 2, 4, -1, -1]
|
||||
|
||||
dailyTemperatures [73, 74, 75, 71, 69, 72, 76, 73]
|
||||
= [1, 1, 4, 2, 1, 1, 0, 0]
|
||||
|
||||
nextGreaterCircular [1, 2, 1] = [2, -1, 2]
|
||||
nextGreaterSubset [4, 1, 2] within [1, 3, 4, 2] = [-1, 3, -1]
|
||||
@@ -0,0 +1,25 @@
|
||||
Largest rectangle in a histogram: pops, widths and areas
|
||||
========================================================
|
||||
|
||||
heights [2, 1, 5, 6, 2, 3]
|
||||
i=0 height=2:
|
||||
push index 0 stack(top first) = [0]
|
||||
i=1 height=1:
|
||||
pop bar of height 2: left edge index -1, right edge index 0, width 1, area 2 best = 2
|
||||
push index 1 stack(top first) = [1]
|
||||
i=2 height=5:
|
||||
push index 2 stack(top first) = [2, 1]
|
||||
i=3 height=6:
|
||||
push index 3 stack(top first) = [3, 2, 1]
|
||||
i=4 height=2:
|
||||
pop bar of height 6: left edge index 2, right edge index 3, width 1, area 6 best = 6
|
||||
pop bar of height 5: left edge index 1, right edge index 3, width 2, area 10 best = 10
|
||||
push index 4 stack(top first) = [4, 1]
|
||||
i=5 height=3:
|
||||
push index 5 stack(top first) = [5, 4, 1]
|
||||
i=6 (sentinel) height=0:
|
||||
pop bar of height 3: left edge index 4, right edge index 5, width 1, area 3 best = 10
|
||||
pop bar of height 2: left edge index 1, right edge index 5, width 4, area 8 best = 10
|
||||
pop bar of height 1: left edge index -1, right edge index 5, width 6, area 6 best = 10
|
||||
push index 6 stack(top first) = [6]
|
||||
largest rectangle = 10
|
||||
@@ -0,0 +1,16 @@
|
||||
Queue from two stacks, and stack from one queue
|
||||
===============================================
|
||||
|
||||
QueueViaStacks (stacks shown top first)
|
||||
enqueue(1) in=[1] out=[]
|
||||
enqueue(2) in=[2, 1] out=[]
|
||||
enqueue(3) in=[3, 2, 1] out=[]
|
||||
dequeue() = 1 in=[] out=[2, 3] transfers so far: 3
|
||||
enqueue(4) in=[4] out=[2, 3]
|
||||
dequeue() x3 = 2, 3, 4 in=[] out=[] transfers so far: 4
|
||||
|
||||
StackViaQueue
|
||||
push(1) queue(front=top)=[1] rotations so far: 0
|
||||
push(2) queue(front=top)=[2, 1] rotations so far: 1
|
||||
push(3) queue(front=top)=[3, 2, 1] rotations so far: 3
|
||||
pop() x2 = 3, 2 queue(front=top)=[1]
|
||||
@@ -0,0 +1,13 @@
|
||||
All ten problems on their headline examples
|
||||
===========================================
|
||||
|
||||
1. isBalanced("{[()]}") = true; isBalanced("([)]") = false
|
||||
2. evaluate([2, 1, +, 3, *]) = 9; 7 / -3 truncates toward zero: -2
|
||||
3. push -2, 0, -3: getMin = -3; pop; top = 0, getMin = -2 (both implementations agree)
|
||||
4. nextGreater([1, 3, 4, 2]) = [3, 4, -1, -1]
|
||||
5. dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73]) = [1, 1, 4, 2, 1, 1, 0, 0]
|
||||
6. nextGreaterCircular([1, 2, 3, 4, 3]) = [2, 3, 4, -1, 4]
|
||||
7. largestRectangle([2, 1, 5, 6, 2, 3]) = 10
|
||||
8. decode("3[a2[bc]]") = "abcbcabcbcabcbc"; decode("2[abc]3[cd]ef") = "abcabccdcdcdef"
|
||||
9. enqueue a, b, c; dequeue = a; enqueue d; dequeue = b; peek = c; size = 2
|
||||
10. push a, b, c; top = c; pop = c; pop = b; size = 1
|
||||
@@ -0,0 +1,12 @@
|
||||
Random agreement with brute force (seed 20261112, 5000 cases per row)
|
||||
=====================================================================
|
||||
|
||||
nextGreater vs scan-right 5000 cases, 0 mismatches
|
||||
dailyTemperatures vs scan-right 5000 cases, 0 mismatches
|
||||
nextGreaterCircular vs wrap-around scan 5000 cases, 0 mismatches
|
||||
largestRectangle vs widen-each-bar 5000 cases, 0 mismatches
|
||||
isBalanced vs delete-adjacent-pairs 5000 cases, 0 mismatches
|
||||
decode vs recursive descent 5000 cases, 0 mismatches
|
||||
evaluate (RPN) vs expression-tree evaluation 5000 cases, 0 mismatches
|
||||
MinStack and EncodedMinStack vs recompute-the-min 5000 sequences (150000 operations), 0 mismatches
|
||||
QueueViaStacks and StackViaQueue vs ArrayDeque 5000 sequences (150000 operations), 0 mismatches
|
||||
@@ -0,0 +1,24 @@
|
||||
Operation counts (counted, not timed)
|
||||
=====================================
|
||||
|
||||
nextGreater on a strictly decreasing array (the worst case for the scan-right version)
|
||||
n scan-right compares stack compares stack push+pop
|
||||
1000 499500 999 1000
|
||||
10000 49995000 9999 10000
|
||||
100000 4999950000 99999 100000
|
||||
|
||||
nextGreater on random values (n = 100000): every index is pushed once and popped at most once
|
||||
pushes = 100000, pops = 99985, comparisons = 199977 (bound: comparisons <= 2n = 200000)
|
||||
|
||||
QueueViaStacks: n enqueues followed by n dequeues
|
||||
n total element moves moves in the 1st dequeue
|
||||
1000 1000 1000
|
||||
10000 10000 10000
|
||||
100000 100000 100000
|
||||
one dequeue can cost n moves, but each element moves exactly once, so n enqueues + n dequeues cost 3n steps in all
|
||||
|
||||
StackViaQueue: n pushes
|
||||
n total rotations n(n-1)/2
|
||||
100 4950 4950
|
||||
1000 499500 499500
|
||||
5000 12497500 12497500
|
||||
@@ -0,0 +1,19 @@
|
||||
java.util.Stack versus ArrayDeque used as a stack
|
||||
=================================================
|
||||
|
||||
ArrayDeque as a stack: push(1), push(2), push(3), then pop until empty -> [3, 2, 1]
|
||||
ArrayDeque as a queue: offer(1), offer(2), offer(3), then poll until empty -> [1, 2, 3]
|
||||
|
||||
push(1), push(2), push(3), then iterate:
|
||||
java.util.Stack -> [1, 2, 3] (bottom to top)
|
||||
ArrayDeque -> [3, 2, 1] (top to bottom)
|
||||
|
||||
Stack.class.getSuperclass() = java.util.Vector
|
||||
Stack.pop() is synchronized = true
|
||||
ArrayDeque.pop() is synchronized = false
|
||||
|
||||
push(null) on Stack = accepted
|
||||
push(null) on ArrayDeque = java.lang.NullPointerException
|
||||
|
||||
ArrayDeque: push(1), push(2), peek() = 2 (push is addFirst)
|
||||
ArrayDeque: offer(1), offer(2), peek() = 1 (offer is addLast)
|
||||
@@ -0,0 +1,33 @@
|
||||
Eight classic mistakes, each reproduced
|
||||
=======================================
|
||||
|
||||
1. balanced brackets by counting each kind, with no stack
|
||||
input "([)]": counts-only = true stack = false
|
||||
every count ends at zero, but the ] arrives while the ( is not the innermost open bracket
|
||||
|
||||
2. min stack that compares boxed Integers with ==
|
||||
push [10, 20, 5], pop, getMin(): broken = 10 correct = 10
|
||||
push [1000, 2000, 500], pop, getMin(): broken = 500 correct = 1000
|
||||
Integer.valueOf caches -128..127, so small values share one object and == happens to work
|
||||
|
||||
3. min stack that pushes onto the min stack only on a strictly smaller value
|
||||
push 3, push 3, pop, getMin(): strict -> NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Deque.peek()" is null
|
||||
correct -> 3 (the stack still holds one 3)
|
||||
|
||||
4. encoded min stack with int differences instead of long
|
||||
push Integer.MIN_VALUE, push Integer.MAX_VALUE, top(): int diffs = -2147483648 long diffs = 2147483647
|
||||
MAX_VALUE - MIN_VALUE does not fit in an int, wraps to -1, and -1 looks like "new minimum"
|
||||
|
||||
5. next greater element popping on <= instead of <
|
||||
input [2, 2, 3]: buggy = [2, 3, -1] correct = [3, 3, -1]
|
||||
the first 2 is told its next greater element is 2
|
||||
|
||||
6. largest rectangle without the final height-0 bar
|
||||
input [2, 4]: buggy = 0 correct = 4
|
||||
nothing ever pops the bars that are still on the stack when the loop ends
|
||||
|
||||
7. queue from two stacks that pours on every dequeue
|
||||
enqueue 1, 2; dequeue; enqueue 3; dequeue: first = 1 (ok), second: broken = 3 correct = 2
|
||||
the 3 was poured on top of the 2 that was still waiting in the output stack
|
||||
|
||||
8. popping an empty structure: both conversions throw NoSuchElementException, as ArrayDeque.pop does
|
||||
Reference in New Issue
Block a user