最小値を並べて持ち、いちばん小さい値をすぐ返すスタックを作る

設計問題 78

積む、​取り出す、​いちばん上を​見る、​いま入っている​中の​いちばん​小さい​値を​見る、の​ 4 つの​操作を、​どれも​ O(1) で​こな​すスタックを​作ります。​積むたびに​「ここまでの​最小値」も​一緒に​覚えておく​練習です。

海外で定番の問題リスト 4 本のうち 3 本に入っています (NeetCode 150、Grind 75、LeetCode Top Interview 150)

問題

整数を積んでいくスタックを、クラス MinStack として作ってください。ふつうのスタックの操作に加えて、いま入っている値のうちいちばん小さい値を返す操作を持ちます。次の 4 つの操作を、どれも O(1) の時間でこなしてください。

  • push(x) は、整数 x をいちばん上に積みます。何も返しません
  • pop() は、いちばん上の値を取り除きます。何も返しません
  • top() は、いちばん上の値を返します。取り除きはしません
  • getMin() は、いま入っている値のうち、いちばん小さい値を返します

メソッドの名前は、JavaScript は上のまま、Python は get_min のように小文字と下線、Go は Push、Pop、Top、GetMin と頭を大文字にします。Go では newMinStack() で作ります。

入力は、呼ぶ操作の名前の並び ops と、それぞれの引数の並び args で渡します。ops の最初はクラスの名前で、args の最初は空の並びです。出力は操作ごとの戻り値の並びで、作った所と push、pop は null です。

  • pop、top、getMin は、スタックが空でないときだけ呼びます
  • x は -2147483648 以上 2147483647 以下です
  • 操作の回数は 30000 回以下です

入力と出力の例

例 1

5、3、8 と積むと、いちばん小さいのは 3 です。8 を取り除いても 3 のまま、3 を取り除くと 5 に戻ります。

入力

ops = ["MinStack", "push",
  "push", "push", "getMin",
  "pop", "getMin", "pop",
  "getMin", "top"]
args = [[], [5], [3], [8],
  [], [], [], [], [], []]

出力

[null, null, null, null, 3,
 null, 3, null, 5, 5]

例 2

いちばん小さい値が 2 つ積まれていると、1 つ取り除いても、もう 1 つが残ります。

入力

ops = ["MinStack", "push",
  "push", "getMin", "pop",
  "getMin", "top"]
args = [[], [2], [2], [],
  [], [], []]

出力

[null, null, null, 2, null,
 2, 2]

例 3

負の数も入ります。

入力

ops = ["MinStack", "push",
  "push", "top", "getMin",
  "push", "pop", "pop",
  "getMin"]
args = [[], [-1], [-6], [],
  [], [0], [], [], []]

出力

[null, null, null, -6, -6,
 null, null, null, -1]

面接での聞かれ方

面接では、「元に戻す」の履歴や、関数の呼び出しの積み重ねを例に出して渡されることが多いです。

  • 「いちばん小さい値を、毎回全部見ずに返してください」と、O(1) が条件に入ります
  • 最小値を 1 つの置き場だけで持つ書き方を出すと、「その値を取り除いたら、次の最小値はどうやって知りますか?」と聞かれます
  • 書き終えたあとに「記憶をもっと減らせますか?」「同じ値が何度も積まれたらどうなりますか?」と続きます

最小値は、取り除くたびに前の状態へ戻らなければなりません。「戻る先を、積むときに一緒に覚えておく」と気づけるかが芯です。

アルゴリズムのチートシートの「ハッシュマップ」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
class MinStack:
    def __init__(self):
        # ここに持つ物を書きます
        pass

    def push(self, x):
        # ここに書きます
        pass

    def pop(self):
        # ここに書きます
        pass

    def top(self):
        # ここに書きます
        pass

    def get_min(self):
        # ここに書きます
        pass


# 「実行」を押すと、下の行が動きます
obj = MinStack()
print(obj.push(5))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します