速い指と遅い指で連結リストの輪を見つける

連結リストを​たどると​同じ​節へ​戻ってくる輪が​あるかを​返します。​1 つずつ​進む指と​ 2 つずつ​進む指を​走らせ、​追いつくか​どうかで​見分ける​練習です。

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

問題

連結リストの先頭の節 head を受け取ります。next をたどっていくと、いつか前に通った節へ戻ってくる (輪がある) なら真、最後の節にたどり着いて終わるなら偽を返す関数を書いてください。

  • 各節は val (値) と next (次の節) を持ちます。Go の節は ListNode で、Val、Next と大文字で始まります
  • 例の pos は、輪の作り方を表す数です。最後の節の next が、先頭から数えて pos 番 (0 から数える) の節へつながっています。pos が -1 なら輪はありません
  • pos は関数へは渡りません。 関数が受け取るのは head だけで、輪があるかどうかは節をたどって自分で見分けます
  • 節の数は 0 以上 10000 以下、値はどれも -100000 以上 100000 以下です。違う節が同じ値を持つこともあります

例では、リストを先頭から読んだ値の並びと pos で書きます。

入力と出力の例

例 1

最後の 9 の次が、1 番の 7 へ戻ります。7、1、9 が輪になっています。

入力

head = [4, 7, 1, 9]
pos = 1

出力

true

例 2

輪がなく、2 で終わります。

入力

head = [6, 2]
pos = -1

出力

false

例 3

節が 1 つで、自分自身へつながっています。

入力

head = [3]
pos = 0

出力

true

面接での聞かれ方

連結リストの問題の中でも、よく出る 1 問です。

  • 「連結リストに輪があるかを判定してください」
  • 見た節を集合に覚える書き方を出すと、ほぼ必ず「追加のメモリを O(1) にできますか?」と続きます
  • 書き終えたあとに「輪の入り口の節を返せますか?」「速い指は、なぜ遅い指を飛び越さずに必ず追いつくのですか?」と聞かれることもあります

ジュニアの問題なので、速い指が先へ進むときに空の節を読まない書き方と、追いつく理由を短く言えるかが見られます。

アルゴリズムのチートシートの「配列と文字列」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def has_cycle(head):
    # 連結リストの節は val、next を持ちます。最後の next は None です
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
# build_list は並びから連結リストを組みます
print(has_cycle(build_list([4, 7, 1, 9], 1)))

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