トポロジカルソートとヒープで講座を受ける順を決める

「この​講座の​前に​あの​講座を​終える」という​決まりの​並びから、​全部の​講座を​受ける​順を​ 1 つ返す。​いま受けられる​講座の​うち、​番号の​いちばん​小さい​物を​ヒープで​取り出していきます。

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

問題

講座が n 個あり、0 から n - 1 の番号が付いています。前提の決まりの並び reqs を受け取ります。reqs の 1 つ [a, b] は、「講座 a を受ける前に、講座 b を終えておく」という意味です。決まりを全部守って n 個の講座を受ける順を、講座の番号の並びで返す関数を書いてください。

  • 順が何通りもあるときは、いま受けられる講座のうち、番号のいちばん小さい物から受けると決めます。答えはこれで 1 つに決まります
  • 全部を受けることができないときは、空の並び [] を返します。一部だけ受けられても、空の並びです
  • n は 1 以上 2000 以下、reqs の長さは 0 以上 5000 以下です
  • 決まりの a と b は違う番号で、同じ決まりは 2 回出てきません

入力と出力の例

例 1

最初に受けられるのは講座 0 と 1 です。1 を終えると 2 と 4 が受けられ、小さい 2 から受けます。講座 3 は、2 と 4 の両方を終えてから受けます。

入力

n = 5
reqs = [[4, 1], [2, 1],
  [3, 4], [3, 2]]

出力

[0, 1, 2, 4, 3]

例 2

講座 0 は講座 2 が、講座 1 は講座 4 が前提です。最初に受けられるのは 2、3、4 で、小さい 2 から受けます。2 を終えると 0 が受けられるようになり、残っている 3 と 4 より小さいので、先に受けます。

入力

n = 5
reqs = [[1, 4], [0, 2]]

出力

[2, 0, 3, 4, 1]

例 3

講座 0 と 1 が互いに前提になっています。講座 2 は受けられますが、全部は受けられないので空の並びです。

入力

n = 3
reqs = [[0, 1], [1, 0]]

出力

[]

面接での聞かれ方

面接では、全部を受けられるかを判定する問題の続きとして出されることが多いです。

  • 「では、受ける順番を 1 つ返してください。できないときは空で」
  • 書き終えたあとに「順番が何通りもあるとき、番号の小さい順にそろえるにはどうしますか?」「そのとき計算量はどう変わりますか?」と続けて聞かれます
  • 「深さ優先で書くなら、どこで並びに足しますか?」と、別の書き方を聞かれることもあります

順が 1 つに決まらない問題では、どの順を返すかを先に面接官とそろえます。ここでは、番号の小さい物から受けると決めてあります。

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

main.py
def course_order(n, reqs):
    # ここに書きます
    pass


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

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