二分探索で行ごとに並んだ表から値を探す

各行が小さい順に並び、行の頭が前の行の最後より大きい表から、目標の値があるかを返します。表を 1 本の長い並びと見なして二分探索する練習です。

海外で定番の問題リスト 4 本には入っていません

問題

整数の表 matrix と目標の値 target を受け取ります。target が表のどこかにあれば true、無ければ false を返す関数を書いてください。

  • 表は m 行 n 列で、どの行も左から小さい順に並んでいます
  • どの行も、頭の値が 1 つ前の行の最後の値より大きくなっています。つまり、上の行から左から右へ読むと、全体が小さい順の 1 本の並びになります
  • 同じ値は 2 回出てきません
  • m と n はどちらも 1 以上 100 以下、値と target はどれも -10000 以上 10000 以下です
  • 時間は O(log(m × n)) で解いてください

入力と出力の例

例 1

社員番号の表です。上の行から左から右へ読むと 2、5、9、14、17、21、26、30、33、38、41、47 と並び、21 は 2 行目の 2 列目にあります。

matrix = [[2, 5, 9, 14], [17, 21, 26, 30], [33, 38, 41, 47]] target = 21
true

例 2

15 は 1 行目の最後の 14 と 2 行目の頭の 17 のあいだに入るはずの値で、表にはありません。

matrix = [[2, 5, 9, 14], [17, 21, 26, 30], [33, 38, 41, 47]] target = 15
false

例 3

1 列だけの表です。いちばん下の値も見つけられます。

matrix = [[-8], [-3], [4], [10]] target = 10
true

面接での聞かれ方

二分探索の応用として、ミドルの面接でよく出ます。

  • 「行ごとに並んだ表から値を探してください。全部を見るより速くできますか?」
  • 書き終えたあとに「行の頭が前の行の最後より大きい、という決まりが無かったらどうしますか?」と続きを聞かれることがあります。そのときは右上の角から始め、大きければ左へ、小さければ下へ進む O(m + n) の書き方になります
  • 「行で 1 回、列で 1 回の 2 段の二分探索でも書けますか?」と聞かれることもあります

ミドルの問題なので、表の位置と 1 本の並びの番号を行き来する式を、迷わず書けるかが見られます。

ヒント

まだ開いていません

付箋は 4 枚。下の段ほど答えに近づきます

  1. ヒント 2

  2. ヒント 3

  3. ヒント 4答えに近い

アルゴリズムのチートシートの「ソートと二分探索」に、この型の要点を表とコードでまとめています。

二分探索は、並んだデータの範囲を半分ずつ絞ります。100 万件でも 20 回ほどで 1 件になります。

main.py
def sorted_matrix_search(matrix: list[list[int]], target: int) -> bool:
    """
    Goal: 何を返すか

    idea1: まず思いつく案
    -> runtime: O(?)  space: O(?)

    idea2: もっと速い案
    -> runtime: O(?)  space: O(?)

    edge case: 空、1 つだけ、など
    """
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(sorted_matrix_search([[2, 5, 9, 14], [17, 21, 26, 30], [33, 38, 41, 47]], 21))

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