456. 132 Pattern

URL: https://leetcode.com/problems/132-pattern
タイトル: 132 Pattern
難易度:中

タグに二分探索法とついていて、二分探索法を使って解く問題だと思っていましたが、二分探索法は関係なく、単純にスタックを使って解く問題でした。
(本当は二分探索法を使う問題を解きたかった、、)
但し、単純なコードで表せるにもかかわらず、結局自分のコードでは解くことができませんでした。
問題がAcceptedになるにはO(N)の計算量で実装する必要があるのですが、そこまで辿り着くことができませんでした。
今回はNeetCodeの解答を元に解説をしていきたいと思います。

参考のコード解説:132 Pattern - Leetcode 456 - Python - YouTube

問題

Given an array of n integers nums, a 132 pattern is a subsequence of three integers nums[i], nums[j] and nums[k] such that i < j < k and nums[i] < nums[k] < nums[j].

Return true if there is a 132 pattern in nums, otherwise, return false.

訳:
n個の整数の配列numsが与えられる。
132パターンとは、nums[i], nums[j], nums[k]の3つの整数の部分列で、
i < j < k かつ nums[i] < nums[k] < nums[j] となるものである。
numsに132パターンがあればtrueを返し、なければfalseを返せ。

sequence… 順序付けられたものの集まり

コード

# ローカルで実行するため、引数を変更しています。
def find132pattern(nums):
  stack = []
  curMin = nums[0]
  for n in nums[1:]:
    while stack and n >= stack[-1][0]:
      stack.pop()
    if stack and n > stack[-1][1]:
      return True
    stack.append([n,curMin])
    curMin = min(curMin,n)
  return False

手順

① 初期値の設定
② Forループの作成
③ スタックに値を格納
④ 最小値を更新
⑤ スタックから値を取り出す
⑥ 132パターンの判定

① 初期値の設定

stack:
値を格納していくスタックを用意します。
初期値は空のリストです。

curMin:
現在の最小値を格納していく変数を用意します。
初期値は配列の最初の要素とします。

def find132pattern(nums):

  # 初期値の設定
  stack = [] # 空のリストを用意
  curMin = nums[0] # 配列の最初の要素を用意

② Forループの作成

処理を実行するためのForループを作成します。
範囲は配列の2番目から最後までとします。
配列の2番目からとする理由は既に最初の要素をcurMinに格納しているためです。

def find132pattern(nums):
  stack = []
  curMin = nums[0]

  # Forループの作成
  for n in nums[1:]:

③ スタックに値を格納

スタックに値を格納する処理を実装します。
最初のループではstackに[2,1]が格納され、
stack = [[2,1]]
となります。

def find132pattern(nums):
  stack = []
  curMin = nums[0]

  for n in nums[1:]:

    # スタックに値を格納
    stack.append([n,curMin])

④ 最小値を更新

最小値を更新する処理を実装します。
スタックに追加したばかりのnとcurMinを比較し、
nの方が小さければcurMinをnに更新します。

def find132pattern(nums):
  stack = []
  curMin = nums[0]

  for n in nums[1:]:
    stack.append([n,curMin])

    # 最小値を更新
    curMin = min(curMin,n)

⑤ スタックから値を取り出す

スタックから値を取り出す処理を実装します。
nとスタックの最後の要素の0番目の値を比較します。
nがスタックの最後の要素の0番目の値以上であれば、
スタックから値を取り出します。

def find132pattern(nums):
  stack = []
  curMin = nums[0]

  for n in nums[1:]:

    # スタックから値を取り出す
    while stack and n >= stack[-1][0]:
      stack.pop()

    stack.append([n,curMin])
    curMin = min(curMin,n)

⑥ 132パターンの判定

最後に132パターンの判定を実装します。
スタックの最後の要素の1番目の値とnを比較します。
nがスタックの最後の要素の1番目の値より大きければ、
132パターンが存在すると判定します。

def find132pattern(nums):
  stack = []
  curMin = nums[0]

  for n in nums[1:]:
    while stack and n >= stack[-1][0]:
      stack.pop()

    # 132パターンの判定
    if stack and n > stack[-1][1]:
      return True

    stack.append([n,curMin])
    curMin = min(curMin,n)
  return False

各ループの処理

nums = [1,2,0,3,-1,6,3]として、
実際に各ループの処理を見てみましょう。
考え方としては、各ループでスタックに
左の値(132パターンの最小値)と
真ん中の値(132パターンの最大値)を格納し、
numsの値を右の値(132パターンの中間値)として
判定していきます。

nums = [1,2,0,3,-1,6,3]
  ...
  for n in nums[1:]:
  
    ## 1回目のループ
    # stack = [], curMin = 1, n = 2
    while stack and n >= stack[-1][0]: # False: stackが空
      stack.pop()
    if stack and n > stack[-1][1]: # False: stackが空
      return True
    stack.append([n,curMin]) # stackを更新 -> [[2,1]]
    curMin = min(curMin,n) # curMinを更新 -> 1
  
    ## 2回目のループ
    # stack = [[2,1]], curMin = 1, n = 0
    while stack and n >= stack[-1][0]: # False: 0 >= 2
      stack.pop()
    if stack and n > stack[-1][1]: # False: 0 > 1
      return True
    stack.append([n,curMin]) # stackを更新 -> [[2,1],[0,1]]
    curMin = min(curMin,n) # curMinを更新 -> 0

    ## 3回目のループ
    # stack = [[2,1],[0,1]], curMin = 0, n = 3
    while stack and n >= stack[-1][0]: # True: 3 >= 0 | True: 3 >= 2
      stack.pop() # stackを更新 / 2回pop() -> []
    if stack and n > stack[-1][1]: # False: stackが空
      return True
    stack.append([n,curMin]) # stackを更新 -> [[3,0]]
    curMin = min(curMin,n) # curMinを更新 -> 0

    ## 4回目のループ
    # stack = [[3,0]], curMin = 0, n = -1
    while stack and n >= stack[-1][0]: # False: -1 >= 3
      stack.pop()
    if stack and n > stack[-1][1]: # False: -1 > 0
      return True
    stack.append([n,curMin]) # stackを更新 -> [[3,0],[-1,0]]
    curMin = min(curMin,n) # curMinを更新 -> -1
  
    ## 5回目のループ
    # stack = [[3,0],[-1,0]], curMin = -1, n = 6
    while stack and n >= stack[-1][0]: # True: 6 >= -1 | True: 6 >= 3
      stack.pop() # stackを更新 / 2回pop() -> []
    if stack and n > stack[-1][1]: # False: stackが空
      return True
    stack.append([n,curMin]) # stackを更新 -> [[6,-1]]
    curMin = min(curMin,n) # curMinを更新 -> -1
  
    ## 6回目のループ
    # stack = [[6,-1]], curMin = -1, n = 3
    while stack and n >= stack[-1][0]: # False: 3 >= 6
      stack.pop()
    if stack and n > stack[-1][1]: # True: 3 > -1
      return True # [-1,6,3]で132パターンを検出 / Trueを返す
    
  return False