Stack and Queue Questions Part 2
181. (LC 456) 132 Pattern
456. 132 Pattern Medium
The intuition tells that the problem could be solved either greedy remembering min, max, mid values or using a stack. The fact is, it uses both.
Solution 1 [10]
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
stack = [] # pair [num, curLeftMin], mono-decreasing stack
curMin = nums[0]
for n in nums:
while stack and n >= stack[-1][0]:
stack.pop()
if stack and n < stack[-1][0] and n > stack[-1][1]:
return True
stack.append([n, curMin])
curMin = min(n, curMin)
return False
182. (LC 735) Asteroid Collision
735. Asteroid Collision Medium
Solution 1 [10]
class Solution:
def asteroidCollision(self, asteroids: List[int]) -> List[int]:
stack = []
for a in asteroids:
while stack and a < 0 and stack[-1] > 0: #collision only when a is negative
diff = a + stack[-1]
if diff > 0: #a loses, it gets destroyed
a = 0
elif diff < 0: #a wins
stack.pop()
else: #if a==stack[-1]
a = 0
stack.pop()
if a: #if a was not destroyed in prev steps, add it to stack
stack.append(a)
return stack
My V (LC accepted 70, 90)
class Solution:
def asteroidCollision(self, asteroids: List[int]) -> List[int]:
stk=[]
for a in asteroids:
if stk and stk[-1] > 0 and a < 0: #only if case [+, -] [->,<-]
while stk and a and stk[-1] > 0: #again check [+,-], because stk could be [-2,+1] a=-2 stop after popping +1
if a + stk[-1] == 0:
stk.pop()
a = None
elif abs(a) > abs(stk[-1]):
stk.pop()
elif abs(a) < abs(stk[-1]):
a=None
if a:
stk.append(a)
return stk
183. (LC 394) Decode String
394. Decode String Medium
My V (LC accepted 93,64, same code also 20,99%)
class Solution:
def decodeString(self, s: str) -> str:
stack = []
for c in s:
if c != "]":
if stack and c.isdigit() == True and stack[-1].isdigit() == True:
stack[-1] += c #if several digits num like s='100[ab]'
else:
stack.append(c)
else:
char = ""
while stack[-1] != "[":
char = stack.pop() + char
stack.pop()
char = int(stack.pop()) * char
stack.append(char)
return "".join(stack)
My V2 (LC accepted 20, 50%)
class Solution:
def decodeString(self, s: str) -> str:
stack = []
for c in s:
if c == ']':
letters = ''
while stack:
item = stack.pop()
if item != '[':
letters = item + letters
else:
break
stack.append(letters * int(stack.pop()))
else:
if stack and c.isdigit() and stack[-1].isdigit(): #>1 digits case
stack[-1] += c
else:
stack.append(c)
return ''.join(stack)
Solution 1 [10]
class Solution:
def decodeString(self, s: str) -> str:
stack = []
for char in s:
if char is not "]":
stack.append(char)
else:
sub_str = ""
while stack[-1] is not "[":
sub_str = stack.pop() + sub_str
stack.pop()
multiplier = ""
while stack and stack[-1].isdigit():
multiplier = stack.pop() + multiplier
stack.append(int(multiplier) * sub_str)
return "".join(stack)
184. (LC 895) Maximum Frequency Stack
895. Maximum Frequency Stack Hard
My V (LC accepted 60, 50%)
class FreqStack:
def __init__(self):
self.cnt = {} #{value: freq}
self.freq = {} #{freq: [value1, value2..]}
self.max_freq = 0
def push(self, val: int) -> None:
self.cnt[val] = 1 + self.cnt.get(val, 0)
if self.cnt[val] in self.freq:
self.freq[self.cnt[val]].append(val)
else:
self.freq[self.cnt[val]] = [val]
self.max_freq +=1
def pop(self) -> int:
res = self.freq[self.max_freq].pop()
self.cnt[res] -=1 #<--don't forget to decrement cnt of that value
if len(self.freq[self.max_freq]) == 0:
self.freq.pop(self.max_freq)
self.max_freq -= 1
return res
Solution 1 [10]
class FreqStack:
def __init__(self):
self.cnt = {}
self.maxCnt = 0
self.stacks = {}
def push(self, val: int) -> None:
valCnt = 1 + self.cnt.get(val, 0)
self.cnt[val] = valCnt
if valCnt > self.maxCnt:
self.maxCnt = valCnt
self.stacks[valCnt] = []
self.stacks[valCnt].append(val)
def pop(self) -> int:
res = self.stacks[self.maxCnt].pop()
self.cnt[res] -= 1
if not self.stacks[self.maxCnt]:
self.maxCnt -= 1
return res
NOTE: each new value gets added to the end of the list of the group. So we can pop from a group and it will be the most recent value (relative to other items in the group).
…After pushing all values:
self.stacks = {
1 : [5,4,3,2],
2 : [5,4,3],
3 : [5]
}
185. (LC 901) Online Stock Span
901. Online Stock Span Medium
Solution 1 [10]
class StockSpanner:
def __init__(self):
self.stack = [] # pair: (price, span)
def next(self, price: int) -> int:
span = 1
while self.stack and self.stack[-1][0] <= price:
span += self.stack[-1][1]
self.stack.pop()
self.stack.append((price, span))
return span
My V (LC accepted 98, 95)
class StockSpanner:
def __init__(self):
self.stack = [] #[[price, span], ..]
def next(self, price: int) -> int:
span = 1
while self.stack and self.stack[-1][0] <= price:
span += self.stack[-1][1]
self.stack.pop()
self.stack.append([price, span])
return span
# prices = [100,80,60,70 ..]
# ^
We pop because having [60, 70], we will never get past the bigger value of 70.
186. Implement queue
Implement the basic queue API: enqueue, dequeue.
import collections
class Queue:
def __init__(self):
self._data = collections.deque()
def enqueue(self, v):
self._data.append(v)
def dequeue(self):
return self._data.popleft()
187. (LC 102) Binary Tree Level Order Traversal
102. Binary Tree Level Order Traversal Medium
My V (LC accepted 70, 50)
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
q = collections.deque()
res = []
if root:
q.append(root)
while q:
res.append([])
for i in range(len(q)):
node = q.popleft()
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res[-1].append(node.val)
return res
def binary_tree_depth_order(tree): # tree=root
res = []
if not tree:
return res
curr_depth_nodes = [tree] # initially one value, [3]
while curr_depth_nodes:
res.append(
[curr.val for curr in curr_depth_nodes]
) # appends from front to end of queue
curr_depth_nodes = [ #REPLACE curr values with their children
child
for curr in curr_depth_nodes
for child in (curr.left, curr.right)
if child
]
return res
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
res = []
q = collections.deque()
if root:
q.append(root)
while q:
val = []
for i in range(len(q)):
node = q.popleft()
val.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(val)
return res
# q = [pop, add ]
# ^ --> adding to the end
# popping from front
# 3
# 9 20
# 15 7
188. (LC 622) Design Circular Queue
622. Design Circular Queue Medium
ARRAY
class MyCircularQueue:
def __init__(self, k: int):
self.q = [0] * k
self.front = 0
self.size = 0
self.capacity = k
def enQueue(self, value: int) -> bool:
if self.isFull():
return False
idx = (self.front + self.size) % self.capacity
self.q[idx] = value
self.size += 1
return True
def deQueue(self) -> bool:
if self.isEmpty():
return False
self.front = (self.front + 1) % self.capacity
self.size -= 1
return True
def Front(self) -> int:
return -1 if self.isEmpty() else self.q[self.front]
def Rear(self) -> int:
if self.isEmpty():
return -1
idx = (self.front + self.size - 1) % self.capacity
return self.q[idx]
def isEmpty(self) -> bool:
return self.size == 0
def isFull(self) -> bool:
return self.size == self.capacity
idx = (self.front + self.size) % self.capacity189. (LC 232) Implement Queue using Stacks
232. Implement Queue using Stacks Easy
O(m) because each elem is pushed no more than twice.
# State
# push 1,2,3
# stk1 [1,2,3]
# stk2 []
# pop
# stk1 [] []
# stk2 [3,2,1] [3,2]
# ->1
# push 4,5
# stk1 [4,5]
# stk2 [3,2]
Solution 1 [7]
class MyQueue:
def __init__(self):
self.stk1 = []
self.stk2 = []
def push(self, x: int) -> None:
self.stk1.append(x)
def pop(self) -> int:
self.move()
return self.stk2.pop()
def peek(self) -> int:
self.move()
return self.stk2[-1]
def empty(self) -> bool:
return not self.stk1 and not self.stk2
def move(self):
if not self.stk2:
while self.stk1:
self.stk2.append(self.stk1.pop())
190. Implement a Queue with max API
class QueueWithMax:
def __init__(self):
self.q = collections.deque()
self.qmax = collections.deque()
def enqueue(self, x):
self.q.append(x)
while self.qmax and self.qmax[-1] < x:
self.qmax.pop()
self.qmax.append(x)
def dequeue(self):
if self.q:
res = self.q.popleft()
if res == self.qmax[0]: #leftpop() from maxq only if ==
self.qmax.popleft()
return res
raise IndexError('empty queue')
def return_max(self):
if self.qmax:
return self.qmax[0]
raise IndexError('empty queue')
# enq(1) enq(2) enq(3) deq() enq(4)
# q [1] [1,3] [1,3,6] [3,6] [3,6,4]
# qmax [1] [X,3]* [X,X,6] [6]** [6]
# * if cur value is greater, pop from qmax
# ** if value we dequeue is smaller, don't touch qmax
My V using 2 stacks
class Q:
def __init__(self):
self.stack1 = []
self.stack2 = []
self.maxes = []
def enqueue(self, data):
self.stack1.append(data)
def dequeue(self):
if self.is_empty():
return None
if not self.stack2:
self.move()
value = self.stack2.pop()
if value == self.maxes[-1]:
self.maxes.pop()
return value
def return_max(self):
if self.is_empty():
return None
if not self.maxes:
self.move()
return self.maxes[-1]
def move(self):
while self.stack1:
value = self.stack1.pop()
if not self.maxes or self.maxes[-1] <= value:
self.maxes.append(value)
self.stack2.append(value)
def is_empty(self):
if not self.stack1 and not self.stack2:
return True
return False
q = Q()
q.enqueue(1)
q.enqueue(3)
q.enqueue(5)
q.enqueue(2)
q.enqueue(5)
print(q.stack1)
print(q.return_max())
print(q.dequeue())
print(q.dequeue())
print(q.dequeue())
print(q.return_max())
# [1, 3, 5, 2, 5]
# 5 #max
# 1
# 3
# 5
# 5 #max