Swift教室 Swift と競技プログラミングの教室

01. Linked List Cycle

読了目安 約2分

Linked List / LeetCode 141 (Easy) — 連結リストに循環があるかを速さの違う 2 つのポインタで判定する。

この章の目次

問題

LeetCode 141. Linked List Cycle(Easy / カテゴリ: Linked List)

連結リストは、各ノードが値と「次のノードへの参照」を持つデータ構造です。 その先頭ノード head が与えられます。 next をたどり続けるとどこかのノードへ戻ってくる(循環がある)とき true を、末尾の nil に到達するとき false を返してください。

考え方

ヒント 1

一度訪れたノードをすべて覚えておくと、同じノードに 2 回目に到達した時点で循環だと分かります。 まずはこの方針で考えてみてください。

ヒント 2

追加のメモリなしで解くには、1 歩ずつ進む遅いポインタと 2 歩ずつ進む速いポインタを同時に走らせます。 循環があれば速い方がいつか遅い方に追いつき、なければ速い方が先に nil へ到達します。

Swift 実装のポイント

  • ListNodeclass で定義します。ノードどうしを参照でつなぐため、値型の struct では表現できません。
  • 同じノードかどうかは ===(参照の同一性)で比べます。ListNodeEquatable ではないので == は使えません。
  • ループ条件は fast != nil && fast?.next != nil のように、オプショナルの nil 判定で書きます。
模範解答
Swift
class Solution {
    func hasCycle(_ head: ListNode?) -> Bool {
        var slow = head
        var fast = head
        while fast != nil && fast?.next != nil {
            slow = slow?.next
            fast = fast?.next?.next
            if slow === fast {
                return true
            }
        }
        return false
    }
}

計算量: O(n) 時間、O(1) 追加メモリ。