Ref
Itβs critical to verify the to use stack if
increasing
or decreasing
(aka
monotonic stack
) when solving LC problem
Use cases
big number
(next great number)
2 stack
)# python
# LC 739, LC 503 - Find next `big number`
# ...
= [] # [[idx, val]]
stack for i, val in enumerate(len(tmp)):
while stack and stack[-1][1] < val:
= stack.pop(-1)
_idx, _val = i - _idx
res[tmp[_idx]]
stack.append([i, val]) # ...
// java
// LC 739
for (int j = 0; j < temperatures.length; j++){
int x = temperatures[j];
/**
* NOTE !!!
* 1) while loop
* 2) stack is NOT empty
* 3) cache temperature smaller than current temperature
*
* st.peek().get(0) is cached temperature
*/
while (!st.isEmpty() && st.peek().get(0) < x){
/**
* st.peek().get(1) is idx
*
*/
[st.peek().get(1)] = j - st.peek().get(1);
nextGreater.pop();
st}
increasing
)// java
// LC 239
// LC 496
// ...
// Traverse the array from right to left
for (int i = 0; i < n; i++) {
// Maintain a decreasing monotonic stack
/** NOTE !!! below */
while (!stack.isEmpty() && nums[stack.peek()] <= nums[i]) {
.pop(); // Pop elements from the stack that are smaller or equal to the current element
stack}
// If stack is not empty, the next greater element is at the top of the stack
if (!stack.isEmpty()) {
[i] = nums[stack.peek()];
result}
// Push the current element's index onto the stack
.push(i);
stack}
// ...
// java
for (int j = 0; j < temperatures.length; j++) {
int x = temperatures[j];
/**
* NOTE !!!
* 1) while loop
* 2) stack is NOT empty
* 3) cache temperature greater than current temperature
*
* st.peek().get(0) is cached temperature
*/
/** NOTE !!! below */
while (!st.isEmpty() && st.peek().get(0) > x) {
/**
* st.peek().get(1) is idx
*
*/
[st.peek().get(1)] = j - st.peek().get(1);
nextGreater.pop();
st}
// Push the current temperature and its index to the stack
.push(new Pair<>(x, j));
st}
// java
// LC 232
// V3
// https://leetcode.com/problems/implement-queue-using-stacks/solutions/6579732/video-simple-solution-by-niits-sqaw/
// IDEA: 2 stack
class MyQueue_3{
private Stack<Integer> input;
private Stack<Integer> output;
public MyQueue_3() {
= new Stack<>();
input = new Stack<>();
output }
public void push(int x) {
.push(x);
input}
public int pop() {
/**
* NOTE !!!
*
* 1) before calling pop() directly,
* we firstly call `peak()`
* purpose:
* reset / reassign elements at `output` stack,
* so we can have the element in `queue ordering` in `output` stack
*
* 2) peak() return an integer, but it DOES NOT terminate the pop() execution
* since the `peek()` method is called and NOT assign its result to any object,
* then the `output.pop();` code is executed and return as result
*/
peek();
return output.pop();
}
public int peek() {
if (output.isEmpty()) {
while (!input.isEmpty()) {
.push(input.pop());
output}
}
return output.peek();
}
public boolean empty() {
return input.isEmpty() && output.isEmpty();
}
}
// java
// LC 496
// V0
// IDEA : STACK
// https://www.youtube.com/watch?v=68a1Dc_qVq4
/** NOTE !!!
*
* nums1 is "sub set" of nums2,
* so all elements in nums1 are in nums2 as well
* and in order to find next greater element in nums1 reference nums2
* -> ACTUALLY we only need to check nums2
* -> then append result per element in nums1
*/
/**
*
* Example 1)
*
* nums1 = [4,1,2]
* nums2 = [1,3,4,2]
* x
* x
* x
* x
* st = [1]
* st = [3] map : {1:3}
* st = [4], map : {1:3, 3:4}
* st = [], map : {1:3, 3:4}
*
* so, res = [-1, 3, -1]
*
*
* Example 2)
*
* nums1 = [1,3,5,2,4]
* nums2 = [6,5,4,3,2,1,7]
* x
* x
* x
* x
* x
* x
* x
* x
*
* st = [6], map :{}
* st = [6,5], map :{}
* ..
*
* st = [6,5,4,3,2,1], map = {}
* st = [], map = {6:7, 5:7,4:7,3:7,2:7,1:7}
*
*/
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
if (nums1.length == 1 && nums2.length == 1){
return new int[]{-1};
}
/**
* NOTE !!!
* we use map " collect next greater element"
* map definition : {element, next-greater-element}
*/
Map<Integer, Integer> map = new HashMap<>();
Stack<Integer> st = new Stack<>();
for (int x : nums2){
/**
* NOTE !!!
* 1) use while loop
* 2) while stack is NOT null and stack "top" element is smaller than current element (x) is nums2
*
* -> found "next greater element", so update map
*/
while(!st.isEmpty() && st.peek() < x){
int cur = st.pop();
.put(cur, x);
map}
/** NOTE !!! if not feat above condition, we put element to stack */
.add(x);
st}
//System.out.println("map = " + map);
int[] res = new int[nums1.length];
// fill with -1 for element without next greater element
Arrays.fill(res, -1);
for (int j = 0; j < nums1.length; j++){
if(map.containsKey(nums1[j])){
[j] = map.get(nums1[j]);
res}
}
//System.out.println("res = " + res);
return res;
}
# V0
# IDEA : LC 739, LC 503
class Solution(object):
def nextGreaterElements(self, nums):
# edge case
if not nums:
return
= len(nums)
_len # note : we init res as [-1] * _len
= [-1] * _len
res # note : we use "nums = 2 * nums" to simuldate "circular array"
= 2 * nums
nums = [] # [[idx, val]]
stack for idx, val in enumerate(nums):
while stack and stack[-1][1] < val:
= stack.pop(-1)
_idx, _val """
NOTE !!!
-> we get remainder via "_idx % _len" for handling idx issue
(since we made nums = 2 * nums earlier)
"""
% _len] = val
res[_idx
stack.append([idx, val])return res
non balanced
String# LC 1963. Minimum Number of Swaps to Make the String Balanced
# NOTE !!! below trick will ONLY collect not Balanced ], [
# -> e.g. "]][[" or "]]][[["
= "]]][[["
s = []
stack for i in range(len(s)):
# NOTE HERE !!!
if stack and s[i] == "]":
-1)
stack.pop(else:
stack.append()print (stack)
pre num, pre string
# LC 227, 394
# ...
for i, each in enumerate(s):
# ...
if i == len(s) - 1 or each in "+-*/":
if pre_op == "+":
# ...
elif pre_op == "-":
# ...
elif pre_op == "*":
# ...
elif pre_op == "/":
# ...
"""
NOTE this !!!
"""
= each
pre_op = 0
num # ...
# LC 394 Decode String
# V0
# IDEA : STACK
# NOTE : treat before cases separately
# 1) isdigit
# 2) isalpha
# 3) "["
# 4) "]"
# and define num = 0 for dealing with "100a[b]", "10abc" cases
class Solution:
def decodeString(self, s):
= 0
num = ''
string = []
stack """
NOTE : we deal with 4 cases
1) digit
2) "["
3) alphabet
4) "]"
NOTE :
we use pre_num, pre_string for dealing with previous result
"""
for c in s:
# case 1) : digit
if c.isdigit():
= num*10 + int(c)
num # case 2) : "["
elif c == "[":
stack.append(string)
stack.append(num)= ''
string = 0
num # case 3) : alphabet
elif c.isalpha():
+= c
string # case 4) "]"
elif c == ']':
= stack.pop()
pre_num = stack.pop()
pre_string = pre_string + pre_num * string
string return string
# 496. Next Greater Element I
# V0
# IDEA : STACK (for + while loop)
class Solution(object):
def nextGreaterElement(self, nums1, nums2):
# edge case
if not nums2 or (not nums1 and not nums2):
return nums1
= []
res # NOTE : the trick here (found as a flag)
= False
found for i in nums1:
#print ("i = " + str(i) + " res = " + str(res))
= nums2.index(i)
idx # start from "next" element in nums2
# here we init tmp _nums2
= nums2[idx+1:]
_nums2 # while loop keep pop _nums2 for finding the next bigger element
while _nums2:
= _nums2.pop(0)
tmp # if found, then append to res, and break the while loop directly
if tmp > i:
= True
found
res.append(tmp)break
# if not found, we need to append -1 to res
if not found:
-1)
res.append(= False
found return res
# V0
# IDEA : double for loop (one of loops is INVERSE ORDERING) + case conditions op
class Solution(object):
def nextGreaterElement(self, nums1, nums2):
= [None for _ in range(len(nums1))]
res = []
tmp for i in range(len(nums1)):
### NOTE : from last idx to 0 idx. (Note the start and end idx)
for j in range(len(nums2)-1, -1, -1):
#print ("i = " + str(i) + " j = " + str(j) + " tmp = " + str(tmp))
# case 1) No "next greater element" found in nums2
if not tmp and nums2[j] == nums1[i]:
= -1
res[i] break
# case 2) found "next greater element" in nums2, keep inverse looping
elif nums2[j] > nums1[i]:
tmp.append(nums2[j])# case 3) already reach same element in nums2 (as nums1), pop "last" "next greater element", paste to res, break the loop
elif tmp and nums2[j] == nums1[i]:
= tmp.pop(-1)
_tmp = _tmp
res[i] = []
tmp break
return res
// java
// LC 496
// V0
// IDEA : STACK
// https://www.youtube.com/watch?v=68a1Dc_qVq4
/** NOTE !!!
*
* nums1 is "sub set" of nums2,
* so all elements in nums1 are in nums2 as well
* and in order to find next greater element in nums1 reference nums2
* -> ACTUALLY we only need to check nums2
* -> then append result per element in nums1
*/
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
if (nums1.length == 1 && nums2.length == 1){
return new int[]{-1};
}
/**
* NOTE !!!
* we use map " collect next greater element"
* map definition : {element, next-greater-element}
*/
Map<Integer, Integer> map = new HashMap<>();
Stack<Integer> st = new Stack<>();
for (int x : nums2){
/**
* NOTE !!!
* 1) use while loop
* 2) while stack is NOT null and stack "top" element is smaller than current element (x) is nums2
*
* -> found "next greater element", so update map
*/
while(!st.isEmpty() && st.peek() < x){
int cur = st.pop();
.put(cur, x);
map}
/** NOTE !!! if not feat above condition, we put element to stack */
.add(x);
st}
//System.out.println("map = " + map);
int[] res = new int[nums1.length];
// fill with -1 for element without next greater element
Arrays.fill(res, -1);
for (int j = 0; j < nums1.length; j++){
if(map.containsKey(nums1[j])){
[j] = map.get(nums1[j]);
res}
}
//System.out.println("res = " + res);
return res;
}
# LC 503. Next Greater Element II
# V0'
# IDEA : LC 739
class Solution(object):
def nextGreaterElements(self, nums):
# edge case
if not nums:
return
= len(nums)
_len # note : we init res as [-1] * _len
= [-1] * _len
res # note : we use "nums = 2 * nums" to simuldate "circular array"
= 2 * nums
nums = [] # [[idx, val]]
stack for idx, val in enumerate(nums):
while stack and stack[-1][1] < val:
= stack.pop(-1)
_idx, _val """
NOTE !!!
-> we get remainder via "_idx % _len" for handling idx issue
(since we made nums = 2 * nums earlier)
"""
% _len] = val
res[_idx
stack.append([idx, val])return res
# V0'
# IDEA : STACK + circular loop handling
class Solution:
def nextGreaterElements(self, nums):
### NOTE : since we can search nums circurly,
# -> so here we make a new array (augLst = nums + nums) for that
= nums + nums
augLst = []
stack # init ans
= [-1] * len(nums)
res ### NOTE : we looping augLst with inverse order
for i in range(len(augLst)-1, -1, -1):
### NOTE : if stack and last value in stack smaller than augLst[i], we pop last value from stack
while stack and stack[-1] <= augLst[i]:
stack.pop()### NOTE : the remaining element in stack must fit the condition, so we append it to res
# -> note : append to `i % len(nums)` idx in res
if stack:
% len(nums)] = stack[-1]
res[i ### NOTE : we also need to append augLst[i] to stack
stack.append(augLst[i])return res
// c++
// LC 503. Next Greater Element II
// Algorithm book (labu) p. 276
<int> nextGreaterElements(vector<int> & nums){
vectorint n = nums.size();
// save the result
<int> res(n);
vector<int> s;
stack// `simulate` the stack has length double
for (int i = 2 * n - 1; i >= 0; i --){
while (!s.empty() && s.top() <= nums[i % n]){
.pop();
s}
[i % n] = s.empty() ? -1 : s.top();
res.push(nums[i % n]);
s}
return res;
}
# LC 739. Daily Temperatures
# V0
# IDEA : STACK
# DEMO
# ...: T=[73, 74, 75, 71, 69, 72, 76, 73]
# ...: s=Solution()
# ...: r= s.dailyTemperatures(T)
# ...: print(r)
# ...:
# i : 1, stack : [(73, 0)], res : [0, 0, 0, 0, 0, 0, 0, 0]
# i : 2, stack : [(74, 1)], res : [1, 0, 0, 0, 0, 0, 0, 0]
# i : 5, stack : [(75, 2), (71, 3), (69, 4)], res : [1, 1, 0, 0, 0, 0, 0, 0]
# i : 5, stack : [(75, 2), (71, 3)], res : [1, 1, 0, 0, 1, 0, 0, 0]
# i : 6, stack : [(75, 2), (72, 5)], res : [1, 1, 0, 2, 1, 0, 0, 0]
# i : 6, stack : [(75, 2)], res : [1, 1, 0, 2, 1, 1, 0, 0]
# [1, 1, 4, 2, 1, 1, 0, 0]
class Solution(object):
def dailyTemperatures(self, T):
= len(T)
N = []
stack = [0] * N
res ### NOTE : we only use 1 for loop in this problem
for i, t in enumerate(T):
# if stack is not bland and last temp < current tmpe
# -> pop the stack (get its temp)
# -> and calculate the difference
### BEWARE "while" op
while stack and stack[-1][0] < t:
= stack.pop()[1]
oi = i - oi
res[oi] # no matter any case, we have to insert current temp into stack anyway
# since the result (next higher temp) is decided by the coming temp, rather than current temp
stack.append((t, i))return res
// java
// LC 739
// V0
// IDEA : STACK (MONOTONIC STACK)
// LC 496
public int[] dailyTemperatures(int[] temperatures) {
if (temperatures.length == 1){
return temperatures;
}
/**
* Stack :
*
* -> cache elements (temperature) that DOESN'T have (NOT found) next warmer temperature yet
* -> structure : stack ([temperature, idx])
*/
Stack<List<Integer>> st = new Stack<>(); // element, idx
/** NOTE !!!
*
* can't use map, since there will be "duplicated" temperature
* -> which will cause different val has same key (hashMap key)
*/
//Map<Integer, Integer> map = new HashMap<>(); // {temperature : idx-of-next-warmer-temperature}
/**
* NOTE !!!
*
* we use nextGreater collect answer,
* -> idx : temperature, val : idx-of-next-warmer-temperature
*/
int[] nextGreater = new int[temperatures.length];
Arrays.fill(nextGreater, 0); // idx : temperature, val : idx-of-next-warmer-temperature
for (int j = 0; j < temperatures.length; j++){
int x = temperatures[j];
/**
* NOTE !!!
* 1) while loop
* 2) stack is NOT empty
* 3) cache temperature smaller than current temperature
*
* st.peek().get(0) is cached temperature
*/
while (!st.isEmpty() && st.peek().get(0) < x){
/**
* st.peek().get(1) is idx
*
*/
[st.peek().get(1)] = j - st.peek().get(1);
nextGreater.pop();
st}
List<Integer> cur = new ArrayList<>();
.add(x); // element
cur.add(j); // idx
cur.add(cur);
st}
//System.out.println("nextGreater = " + nextGreater);
return nextGreater;
}
# LC 224 Basic Calculator
# V0'
# IDEA : STACK
# https://leetcode.com/problems/basic-calculator/solution/
class Solution:
def calculate(self, s):
= []
stack = 0
operand = 0 # For the on-going result
res = 1 # 1 means positive, -1 means negative
sign
for ch in s:
if ch.isdigit():
# Forming operand, since it could be more than one digit
= (operand * 10) + int(ch)
operand
elif ch == '+':
# Evaluate the expression to the left,
# with result, sign, operand
+= sign * operand
res
# Save the recently encountered '+' sign
= 1
sign
# Reset operand
= 0
operand
elif ch == '-':
+= sign * operand
res = -1
sign = 0
operand
elif ch == '(':
# Push the result and sign on to the stack, for later
# We push the result first, then sign
stack.append(res)
stack.append(sign)
# Reset operand and result, as if new evaluation begins for the new sub-expression
= 1
sign = 0
res
elif ch == ')':
# Evaluate the expression to the left
# with result, sign and operand
+= sign * operand
res
# ')' marks end of expression within a set of parenthesis
# Its result is multiplied with sign on top of stack
# as stack.pop() is the sign before the parenthesis
*= stack.pop() # stack pop 1, sign
res
# Then add to the next operand on the top.
# as stack.pop() is the result calculated before this parenthesis
# (operand on stack) + (sign on stack * (result from parenthesis))
+= stack.pop() # stack pop 2, operand
res
# Reset the operand
= 0
operand
return res + sign * operand
# python
# LC 227. Basic Calculator II, LC 224. Basic Calculator
# V0
# IDEA : STACK
class Solution:
def calculate(self, s):
= []
stack # NOTE THIS !!
= '+'
pre_op = 0
num for i, each in enumerate(s):
# case 1) : digit
if each.isdigit():
= 10 * num + int(each) # the way to deal with number like "100", "10"...
num if i == len(s) - 1 or each in '+-*/':
"""
NOTE !!! : we deal with "pre_op" (rather than current op)
"""
# case 2) : "+"
if pre_op == '+':
stack.append(num)# case 3) : "-"
elif pre_op == '-':
-num)
stack.append(# case 3) : "*"
elif pre_op == '*':
* num)
stack.append(stack.pop() # case 3) : "/"
elif pre_op == '/':
= stack.pop()
top if top < 0:
int(top / num))
stack.append(else:
// num)
stack.append(top # NOTE this!
= each
pre_op = 0
num return sum(stack)
# Algorithm book (labu) p. 342
# IDEA : STACK + RECURSIVE
# TODO : fix output format
class Solution(object):
def calculate(self, s):
def helper(s):
= []
stack = '+'
sign = 0
num
while len(s) > 0:
= s.pop(0)
c if c.isdigit():
= 10 * num + int(c)
num """
do recursive when meet "("
"""
if c == '(':
= helper(s)
num if (not c.isdigit() and c != ' ') or len(s) == 0:
if sign == '+':
stack.append(num)elif sign == '-':
-num)
stack.append(elif sign == '*':
-1] = stack[-1] * num
stack[elif sign == '/':
-1] = int(stack[-1] / float(num))
stack[= 0
num = c
sign """
end recursive when meet ")"
"""
if c == ')':
break
return sum(stack)
# run the helper func
return helper(list(s))
# V1
# python 3
class Solution:
def calculate(self, s):
= []
stack = '+'
pre_op = 0
num for i, each in enumerate(s):
if each.isdigit():
= 10 * num + int(each) # the way to deal with number like "100", "10"...
num if i == len(s) - 1 or each in '+-*/':
if pre_op == '+':
stack.append(num)elif pre_op == '-':
-num)
stack.append(elif pre_op == '*':
* num)
stack.append(stack.pop() elif pre_op == '/':
= stack.pop()
top if top < 0:
int(top / num))
stack.append(else:
// num)
stack.append(top = each
pre_op = 0
num return sum(stack)
# LC 907. Sum of Subarray Minimums
# V0
# IDEA : increasing stacks
class Solution:
def sumSubarrayMins(self, A):
= len(A), 10**9 + 7
n, mod = [0] * n, [0] * n, [], []
left, right, s1, s2
for i in range(n):
= 1
count while s1 and s1[-1][0] > A[i]:
+= s1.pop()[1]
count = count
left[i]
s1.append([A[i], count])
for i in range(n)[::-1]:
= 1
count while s2 and s2[-1][0] >= A[i]:
+= s2.pop()[1]
count = count
right[i]
s2.append([A[i], count])return sum(a * l * r for a, l, r in zip(A, left, right)) % mod
# LC 735. Asteroid Collision
# V0
class Solution(object):
def asteroidCollision(self, asteroids):
= []
ans for new in asteroids:
while ans and new < 0 < ans[-1]:
if ans[-1] < -new:
ans.pop()continue
elif ans[-1] == -new:
ans.pop()break
else:
ans.append(new)return ans
# LC 1047. Remove All Adjacent Duplicates In String
# V0
# IDEA : STACK
class Solution:
def removeDuplicates(self, x):
# edge
if not x:
return
= []
stack """
NOTE !!! below op
"""
for i in range(len(x)):
# NOTE !!! : trick here : if stack last element == current x's element
# -> we pop last stack element
# -> and NOT add current element
if stack and stack[-1] == x[i]:
-1)
stack.pop(# if stack last element != current x's element
# -> we append x[i]
else:
stack.append(x[i])return "".join(stack)
# V0'
# IDEA : TWO POINTERS
# -> pointers : end, c
class Solution:
def removeDuplicates(self, S):
= -1
end = list(S)
a for c in a:
if end >= 0 and a[end] == c:
-= 1
end else:
+= 1
end = c
a[end] return ''.join(a[: end + 1])
# LC 1209. Remove All Adjacent Duplicates in String II
# V0
# IDEA : STACK
class Solution:
def removeDuplicates(self, x, k):
# edge case
if not x:
return None
= []
stack """
NOTE !!!
1) we use [[element, _count]] format for below op
2) note the case when deal with duplicated elements
if stack and stack[-1][0] == x[i]:
if stack[-1][1] < k-1:
stack[-1][1] += 1
else:
stack.pop(-1)
"""
for i in range(len(x)):
if stack and stack[-1][0] == x[i]:
if stack[-1][1] < k-1:
-1][1] += 1
stack[else:
-1)
stack.pop(else:
1])
stack.append([x[i], #print (">> stack = " + str(stack))
= [x[0]*x[1] for x in stack]
tmp #print (">> tmp = " + str(tmp))
return "".join(tmp)
# V0'
# IDEA : STACK
# NOTE !!! we DON'T need to modify original s, (but maintain an extra stack for duplicated checks)
class Solution:
def removeDuplicates(self, s, k):
= [['#', 0]]
stack for c in s:
#print ("c = " + str(c) + " stack = " + str(stack))
if stack[-1][0] == c:
-1][1] += 1
stack[if stack[-1][1] == k:
stack.pop()else:
1])
stack.append([c, return ''.join(c * k for c, k in stack)
# LC 71. Simplify Path
# V0
# IDEA : STACK
class Solution:
def simplifyPath(self, path: str) -> str:
= path.split('/')
s = []
result for i in range(len(s)):
if s[i] and s[i] != '.' and s[i]!='/' and s[i]!='..':
result.append(s[i])elif s[i] == '..':
if result:
result.pop()
return "/"+"/".join(result)
# LC 155. Min Stack
# V0
# IDEA : STACK
# IDEA :
# -> USE A STACK TO STORAGE MIN VALUE IN THE STACK WHEN EVERY PUSH
# -> SO WE CAN RETURN getMin IN CONSTANT TIEM VIA STACK ABOVE
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
def push(self, x):
if not self.stack:
"""
NOTE : we use stack = [(x, y)]
x is the current element
y in current MIN value in current stack
"""
### note here
self.stack.append((x, x))
### NOTICE HERE
# stack[i][1] save to min value when every push
# so the latest min in stack is at stack[-1][1]
else:
### note here
self.stack.append((x, min(x, self.stack[-1][1])))
def pop(self):
self.stack.pop()
def top(self):
return self.stack[-1][0]
def getMin(self):
# the latest min in stack is at stack[-1][1]
return self.stack[-1][1]
# LC 2104. Sum of Subarray Ranges
# NOTE : there are also brute force, 2 pointers ... approaches
# V0'
# IDEA : monotonic stack
# https://zhuanlan.zhihu.com/p/444725220
class Solution:
def subArrayRanges(self, nums):
= [-float('inf')] + nums + [-float('inf')], [], 0
A, s, res for i, num in enumerate(A):
while s and num < A[s[-1]]:
= s.pop()
j -= (i - j) * (j - s[-1]) * A[j]
res
s.append(i)= [float('inf')] + nums + [float('inf')], []
A, s for i, num in enumerate(A):
while s and num > A[s[-1]]:
= s.pop()
j += (i - j) * (j - s[-1]) * A[j]
res
s.append(i)return res
# LC 84. Largest Rectangle in Histogram
# python
# V1'''
# IDEA : STACK
# https://leetcode.com/problems/largest-rectangle-in-histogram/solution/
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
= [-1]
stack = 0
max_area for i in range(len(heights)):
while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:
= heights[stack.pop()]
current_height = i - stack[-1] - 1
current_width = max(max_area, current_height * current_width)
max_area
stack.append(i)
while stack[-1] != -1:
= heights[stack.pop()]
current_height = len(heights) - stack[-1] - 1
current_width = max(max_area, current_height * current_width)
max_area return max_area
// java
// LC 316
/**
* NOTE
*
* Lexicographically Smaller
*
* A string a is lexicographically smaller than a
* string b if in the first position where a and b differ,
* string a has a letter that appears earlier in the alphabet
* than the corresponding letter in b.
* If the first min(a.length, b.length) characters do not differ,
* then the shorter string is the lexicographically smaller one.
*
*/
// V0-1
// IDEA: STACK (fixed by gpt)
// Time: O(n) β one pass over the string and each character is pushed/popped at most once.
// Space: O(1) β constant space for 26 characters (seen, freq, stack)
/**
* π Example Walkthrough
*
* Input: "cbacdcbc"
* 1. 'c' β Stack: ["c"]
* 2. 'b' < 'c' and 'c' still appears β pop 'c', push 'b'
* 3. 'a' < 'b' β pop 'b', push 'a'
* 4. 'c' > 'a' β push 'c'
* 5. 'd' > 'c' β push 'd'
* 6. 'c' already seen β skip
* 7. 'b' > 'd' β push 'b'
* 8. 'c' > 'b' β push 'c'
*
* Final stack: ['a', 'c', 'd', 'b']
* Lexicographically smallest valid string: "acdb"
*
*/
public String removeDuplicateLetters_0_1(String s) {
if (s == null || s.length() == 0) {
return "";
}
/**
* β’ freq: array to count how many times each letter appears in s.
* β’ We use c - 'a' to map each character to index 0β25 ('a' to 'z').
* β’ This helps us later determine if we can remove a character and see it again later.
*/
int[] freq = new int[26]; // frequency of each character
for (char c : s.toCharArray()) {
[c - 'a']++;
freq}
/**
* β’ Tracks which characters have already been added to the result.
* β’ This ensures we only include each character once.
*
*
* NOTE !!! sean is a `boolean` array
*/
boolean[] seen = new boolean[26]; // whether character is in stack/result
/** NOTE !!!
*
* we init stack here
*
*
* β’ This stack is used to build the final result.
* β’ Weβll maintain characters in order and manipulate
* the top to maintain lexicographical order.
*/
/**
* NOTE !!!
*
* use `STACK`, but NOT use `PQ`
*
*/
Stack<Character> stack = new Stack<>();
/**
* β’ Iterate through the string one character at a time.
* β’ Since weβve now processed c, decrement its frequency count.
*/
for (char c : s.toCharArray()) {
[c - 'a']--; // reduce frequency, since we're processing this char
freq
/**
* β’ If weβve already added this character to the result,
* skip it β we only want one occurrence of each letter.
*/
if (seen[c - 'a']) {
continue; // already added, skip
}
/** NOTE !!!
*
* Now weβre checking:
*
* β’ Is the stack NOT empty?
*
* β’ Is the current character c lexicographically
* smaller than the character at the top of the stack?
*
* β’ Does the character at the top of the stack still
* appear later (i.e., its freq > 0)?
*
* If yes to all, we can:
*
* β’ pop it from the result,
*
* β’ and add it later again in a better
* position (lexicographically smaller order).
*/
// remove characters that are bigger than current AND appear later again
while (!stack.isEmpty() && c < stack.peek() && freq[stack.peek() - 'a'] > 0) {
/**
*
* Remove the character from the stack,
* and mark it as not seen so it can be added again later.
*/
char removed = stack.pop();
[removed - 'a'] = false;
seen}
/**
* β’ Push the current character c to the stack,
* β’ And mark it as seen (i.e., already in the result).
*/
.push(c);
stack[c - 'a'] = true;
seen}
// build result from stack
StringBuilder sb = new StringBuilder();
for (char c : stack) {
.append(c);
sb}
return sb.toString();
}