幅優先探索 (BFS) で東と西の両方の海へ流れる場所を探す

島の​高さの​地図から、​雨水が​東の​海にも​西の​海にも​流れていける​場所を​全部返す。​海の​側から​逆に、​高い​ほう​へ登れる​所を​幅優先で​広げ、​両方から​届いた​場所を​集めます。

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

問題

m 行 n 列の島の地図 heights を受け取ります。heights[r][c] は、r 行 c 列のマスの高さです。島の上の端と左の端は西の海に、下の端と右の端は東の海に面しています。

あるマスに降った雨は、上下左右の隣のマスのうち、高さが同じか低いマスへ流れていけます。端のマスからは、面している海へそのまま流れ込みます。雨水が西の海にも東の海にも流れ込めるマスを、全部返す関数を書いてください。

  • マスは [行, 列] の 2 つの数で表し、行の小さい順、同じ行は列の小さい順に並べて返します
  • m と n は 1 以上 200 以下、高さはどれも 0 以上 100000 以下の整数です

入力と出力の例

例 1

左上の 1 と 2 は、右や下へ続く隣がどれも高いので、東の海へは下っていけません。ほかのマスは両方の海へ流れていけます。

入力

heights = [[1, 2, 3],
  [8, 9, 4],
  [7, 6, 5]]

出力

[[0, 2], [1, 0], [1, 1],
 [1, 2], [2, 0], [2, 1],
 [2, 2]]

例 2

まん中の 1 はくぼみです。周りがどれも高いので、どちらの海へも流れ出せません。周りの 3 は同じ高さのマスを伝って、両方の海へ届きます。

入力

heights = [[3, 3, 3],
  [3, 1, 3],
  [3, 3, 3]]

出力

[[0, 0], [0, 1], [0, 2],
 [1, 0], [1, 2],
 [2, 0], [2, 1], [2, 2]]

例 3

マスが 1 つだけの島です。4 つの端が全部このマスなので、両方の海に面しています。

入力

heights = [[5]]

出力

[[0, 0]]

面接での聞かれ方

面接では、雨水の流れや、山の地図の場面で渡されることが多いです。

  • 「高さの地図で、2 つの海の両方へ水が流れ出せる場所を全部返してください」
  • 書き終えたあとに「マスごとに水を流してみる書き方と比べて、どれだけ速いですか?」「なぜ海の側から逆にたどるのですか?」と続けて聞かれます
  • 「同じ高さのマスへは流れますか?」と、条件の端を確かめられることもあります

同じ高さへ流れるかどうかと、角のマスがどちらの海にも面していることを、書く前に確かめておきます。

アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def both_seas(heights):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(both_seas([[1, 2, 3], [8, 9, 4], [7, 6, 5]]))

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