1 minute read

Monotonic Stack(단조 스택)

  • 단조 스택

    기존의 스택을 모든 원소들이 오름차순이나 내림차순으로 단조롭게 하는 것

    기존 원소들 중 특정 원소를 제거하여 정렬하는 방식

  • 활용

    • 문제 봉착

      LeetCode 901. Online Stock Span 을 풀던 중 문제 발생
      
      ```
      
      class StockSpanner:
      
          def __init__(self):
              self.prices = []
      
              self.count = 0
      
          def next(self, price: int) -> int:
              result = 1
      
              # 1. 스택을 복사함
              stack = self.prices[:]
      
              while stack and stack[-1] <= price:
                  result += 1
                  stack.pop()
      
              self.count += 1
              self.prices.append(price)
      ```
      
      해당 코드에서는 next 가 호출될 때 마다 price 에서 stack 으로 값을 복사함
      
      호출될 때 마다 복사하는 절차를 거치다보니 time exceed 발생
      
    • 문제 해결 실패

      ```
      
      class StockSpanner:
      
          def __init__(self):
              self.prices = []
              # 캐싱용 table 생성
              self.table = {}
              self.count = 0
      
          def next(self, price: int) -> int:
              result = 1
      
              if price in self.table:
                  return self.table[price]
      
              # 1. 스택을 복사함
              stack = self.prices[:]
      
              while stack and stack[-1] <= price:
                  result += 1
                  stack.pop()
      
              self.count += 1
              # 호출될 때 마다 price 의 result 값이 저장됨
              self.table[price] = result
              self.prices.append(price)
      ```
      
      DP 처럼 캐싱을 해서 시간을 절약해보자. 라고 생각했지만 해당 코드처럼 table 에 값을 저렇게 저장하면 prices 가 [90, 90, 90] 같은 형태로 올 경우 의미가 없어짐
      
    • Monotonic Stack 을 활용한 문제 해결

      
          class StockSpanner:
      
              def __init__(self):
                  self.monotone_stack = []
      
              # 100, 80, 60, 70, 60, 75, 85
              def next(self, price: int) -> int:
                  # [(100, 1),(80, 1), (60, 1)]
                  # [(100, 1), (80, 1), (70, 2)]
                  # [(100, 1), (80, 1), (70, 2), (60, 1)]
                  # [(100, 1), (80, 1), (75, 4)]
                  # [(100, 1), (85, 6)]
                  stack = self.monotone_stack
      
                  cur_price_quote, cur_price_span = price, 1
      
                  while stack and stack[-1][0] <= cur_price_quote:
                      prev_price_quote, prev_price_span = stack.pop()
      
                      cur_price_span += prev_price_span
      
                  stack.append((cur_price_quote, cur_price_span))
      
                  return cur_price_span
      
      

      monotonic stack 을 이용해 stack 자체를 단순히 함

      stack 은 단순해지지만 span 값은 stack 내부의 자료구조에 저장되므로 stack 을 복사하지 않고 계속 사용할 수 있음