Pythonで解いたLeetCodeの問題をSwiftで解いてみよう「141. Linked List Cycle」

# 概要
[ゼロから始めるLeetCode](https://qiita.com/KueharX/items/6ee2502c3b620c795b70)の時に解いた問題とかをSwiftで書いてみることにした。
シリーズ化するかは不明。

# 問題
[141. Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/)

LinkedList(連結リスト)が循環する構造ならTrue,ないならFalseを返してね、という問題

# Pythonで実装

2つの方法で解いた。

```Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        seen = set()
        while head != None:
            if head in seen:
                return True
            seen.add(head)
            head = head.next
        return False
# Runtime Details 62ms Beats 29.69%of users with Python3
# Memory Details 20.78MB Beats 12.82%of users with Python3
```

まずは単純なset()を用いた解き方。説明文の
>There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer.

箇所から分かるように、LinkedListの中にサイクルがあるということは、過去に出てきた要素が再びheadに入ってくるので、headの情報をsetの中に保存し、あとはひたすらhead.nextを続けてあげる。

そうすることで、もしサイクルがある場合にはsetに保存されている情報と同様の内容が再びheadとして回ってくることになる。
もしheadがNoneになればそれはサイクルが存在しないのでFalseを返す。

こちらの解法は単純で実装しやすいが、空間計算量がO(n)になるという欠点がある。
計算量はO(n)。

そこで二つ目の解法。
[フロイドの循環検出法(ウサギと亀のアルゴリズム)](https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%AD%E3%82%A4%E3%83%89%E3%81%AE%E5%BE%AA%E7%92%B0%E6%A4%9C%E5%87%BA%E6%B3%95)を使っていて、こちらも比較的直感的に書ける。
※ざっくり説明すると、速いポインタと遅いポインタの二つを用いて数列や単方向のLinkedListに循環があるかを特定するアルゴリズム。SolutionsではTwo Poninterと書かれていることが多い。

```Python
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if head is None:
            return False

        toroise = head
        hare =  head.next

        while toroise != hare:
            if hare is None or hare.next is None:
                return False
            toroise = toroise.next
            hare = hare.next.next
        return True
# Runtime Details 60ms Beats 43.40% of users with Python3
# Memory Details 20.12MB Beats 93.98%of users with Python3
```

こちらは計算量は最初の解法と同様にO(n)だが、空間計算量がO(1)と定数になるのが大きい。


# Swiftで実装

上記と同様の内容をSwiftで書き直してみる。
例えば一つ目のSetを使った解法だが、

```Swift
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init(_ val: Int) {
 *         self.val = val
 *         self.next = nil
 *     }
 * }
 */
class Solution {
    func hasCycle(_ head: ListNode?) -> Bool {
        var seen: Set<ListNode> = Set()

        var currentNode = head
        while currentNode != nil {
            if seen.contains(currentNode) {
                return true
            }
            seen.insert(currentNode)
            currentNode = currentNode?.next
        }
        return false
    }
}
```

:::note alert
Line 15: Char 25: error: type 'ListNode' does not conform to protocol 'Hashable' in solution.swift
:::

そもそもListNodeがHashableに準拠していないため、この書き方はできない。

一応、ListNode型の変数を用意して以下のような書き方もできるが、遅い。

```swift
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init(_ val: Int) {
 *         self.val = val
 *         self.next = nil
 *     }
 * }
 */

class Solution {
    func hasCycle(_ head: ListNode?) -> Bool {
        var nodesSeen:[ListNode] = [ListNode]()
        var currentNode = head

        while currentNode != nil {
            if nodesSeen.contains(where: { $0 === currentNode }) {
                return true
            }
            nodesSeen.append(currentNode!)
            currentNode = currentNode?.next
        }
        return false
    }
}
# Runtime Details 122ms Beats 5.23%of users with Swift
# Memory Details 16.07MB Beats 6.65%of users with Swift
```

これでは正直微妙なので、二つ目の解法を使用して書いてみることに。

```Swift
class Solution {
    func hasCycle(_ head: ListNode?) -> Bool {
        if head == nil{
            return false
        }
        var tortoise = head
        var hare = head!.next

        while tortoise != hare {
            if hare == nil || hare?.next == nil {
                return false
            }
            tortoise = tortoise?.next
            hare = hare?.next?.next
        }
        return true
    }
}
```

サクッと書いて、Runしてみる。

:::note alert
Line 21: Char 24: error: operator function '!=' requires that 'ListNode' conform to 'Equatable' in solution.swift
        while tortoise != hare {
:::

・・・あれ？
となったが、そもそもSwiftではListNodeには`Equatable`に準拠していない。
`Equatable`つけていない場合、`hare`,`tortoise`のように変数を代入したときにこのような比較の仕方はできない。

なので、以下のように参照の不等号比較を行うことで無事通るようになる。

```swift
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     public var val: Int
 *     public var next: ListNode?
 *     public init(_ val: Int) {
 *         self.val = val
 *         self.next = nil
 *     }
 * }
 */

class Solution {
    func hasCycle(_ head: ListNode?) -> Bool {
        if head == nil{
            return false
        }
        var tortoise = head
        var hare = head!.next

        while tortoise !== hare {
            if hare == nil || hare?.next == nil {
                return false
            }
            tortoise = tortoise?.next
            hare = hare?.next?.next
        }
        return true
    }
}
# Runtime Details 49ms Beats 82.55%of users with Swift
# Memory　Details　15.70MB　Beats 52.02%of users with Swift
```

Swiftを使い始めたばかりで、Pythonのように思いついた解き方をスラスラと書けないもどかしさがある一方でやはり新しい言語の仕様を勉強する楽しさもあるので、今後も暇があれば書いていきたい。