diff --git a/141_Linked_List_Cycle/note.md b/141_Linked_List_Cycle/note.md new file mode 100644 index 0000000..d405541 --- /dev/null +++ b/141_Linked_List_Cycle/note.md @@ -0,0 +1,53 @@ +# 141. Linked List Cycle + + + +## step1(まず通す) + +イメージとしては「速いやつと遅いやつ2人が同じ道を同じ方向に進んで、速いやつが追いついたらループしてる、そうでないならループしてない」(もともと知っていた) + +fast は fast.next が None の場合に注意してないと None.next をやってエラーが出る + +やったミス: + +- 進めるのと判定するのの順序を間違えて全部 head == head で True になる +- fast を一つしか進めない + +↑頭の中で挙動をシミュレートする力が弱っているので鍛え直す必要がある。 + +## step2(整形&他の人のコードを読む) + +### 他のコード見る前 + +head , head.next, head.next.next が None の場合は早期リターンして +slow, fast を始めから1ステップ進めた状態で定義、ループの中では判定→進めるの順でやることも考えたけど、同じような条件式が2回出てくるのは見づらいだろうということで Step1のスタイルにした。 + +### 見たあと + +参考になったコード・コメント・ドキュメント + +- [他の方のメモ](https://github.com/naoto-iwase/leetcode/pull/1/changes/BASE..21cd6c5ca124e1b81b91b8003681e1a49b1c5927#diff-bb5a355bc7de70630b57c0d17d649e9adce45b5f0965089f22bf15dd4236d47fR75) + - [公式ドキュメント](https://docs.python.org/ja/3.13/library/stdtypes.html#comparisons) に「あるクラスの同一でないインスタンスは、通常等価でないとされますが、そのクラスが __eq__() メソッドを定義している場合は除きます。」「is および is not 演算子の振る舞いはカスタマイズできません。また、これらはいかなる 2 つのオブジェクトにも適用でき、決して例外を送出しません。」とあり、`==` は「(勝手に定義した)何らかの意味」で等しいというふうにできるのでオブジェクトが同一か知りたい今回は `is` を使うべき +- + - 気になるのは「fast は slow に追いついたか?」なので、fast is slow としたい +- + - 知ってたので Floyd の方法でやったが、「今見ているノードはすでに到達したか?」と考えて set でやる方が応用の効く(普通の)発想かもしれない + - set はハッシュマップだったはずなので `in` の判定も(衝突がなければ)O(1)だし、リストを線形に舐めるだけなのは変わらなそう + - [公式ドキュメント](https://docs.python.org/ja/3/library/stdtypes.html#set)にも「集合の要素は ハッシュ可能 なものでなくてはなりません」とあるし調べた感じそうらしいので間違いなさそう(本当は実装を読むべきなのでしょうが今日は時間がない) + - while の行に書く条件と while の中でループを抜けるために書く条件(少なくとも先頭か末尾なら)は、while を抜けるという意味で等価なのでどこに書くかのバリエーションが出る、という理解 + - 今回は、「終端に着くまで毎回『通った道じゃないよな?』と確認する」という気持ちだったので step2_set.py のように書いた + - + - Python では再起が好まれない話 + - デフォルトで深さが1000 + - 再帰的に定式化するとコードもそのまま書けるのでわかりやすい、という良さはあるがいつでも使えば良いってもんじゃない + - Python3 は末尾再帰最適化が効かないっぽい? +- [他の方のコード](https://github.com/shintaro1993/arai60/pull/3/changes#diff-491120a14a1981900662359b52a1c34fca42db491aff99fb79c9ba09193ba12fR109) + - fast が先に行ってくれており slow の next は None じゃないことが保証されているので `slow is not None` は書かなくても正しく動きはする + - ただしこれだと Mypy(型チェッカー) は赤線引いてくる(たぶん単純な型推論だとわからなくて、コードの内容くわしく見ないといけないから) + - どっちが好まれる? + - 一応チェックない方が O(n) の定数倍が削れるし単純に `and` で繋ぐ数が少なくて見やすい気がする + +## step3(10分以内にさっとかける * 3回) + +個人的に書いてて「自然」に感じられるのは set 使う方、という感想。 +Floyd の方は「へーこんなやり方が」って感じな一方 set 使う方は「まあそうだよな」って感じで、おそらく後者の方がいい場面が多いので。 diff --git a/141_Linked_List_Cycle/step1.py b/141_Linked_List_Cycle/step1.py new file mode 100644 index 0000000..8b83194 --- /dev/null +++ b/141_Linked_List_Cycle/step1.py @@ -0,0 +1,27 @@ +from typing import Optional + + +# 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: + # リストを先頭から一つずつ歩いていくやつと1つ飛ばし(2倍速)で歩いていくやつがいて + # 速いやつが遅いやつに追いついたらループしてる + # どこかで None が出てきたらループしてない(そこが終端) + + slow = head + fast = head + + while slow is not None and fast is not None and fast.next is not None: + slow = slow.next + fast = fast.next.next + + if slow == fast: + return True + + return False diff --git a/141_Linked_List_Cycle/step2_floyd.py b/141_Linked_List_Cycle/step2_floyd.py new file mode 100644 index 0000000..41f7ae1 --- /dev/null +++ b/141_Linked_List_Cycle/step2_floyd.py @@ -0,0 +1,27 @@ +from typing import Optional + + +# 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: + # リストを先頭から一つずつ歩いていくやつと1つ飛ばし(2倍速)で歩いていくやつがいて + # 速いやつが遅いやつに追いついたらループしてる + # どこかで None が出てきたらループしてない(そこが終端) + + slow = head + fast = head + + while fast is not None and fast.next is not None: + slow = slow.next + fast = fast.next.next + + if fast is slow: + return True + + return False diff --git a/141_Linked_List_Cycle/step2_set.py b/141_Linked_List_Cycle/step2_set.py new file mode 100644 index 0000000..9421e8e --- /dev/null +++ b/141_Linked_List_Cycle/step2_set.py @@ -0,0 +1,23 @@ +from typing import Optional + + +# 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: + current_node = head + visited_nodes = set() + + while current_node is not None: + visited_nodes.add(current_node) + current_node = current_node.next + + if current_node in visited_nodes: + return True + + return False diff --git a/141_Linked_List_Cycle/step3_floyd.py b/141_Linked_List_Cycle/step3_floyd.py new file mode 100644 index 0000000..a07ae47 --- /dev/null +++ b/141_Linked_List_Cycle/step3_floyd.py @@ -0,0 +1,23 @@ +from typing import Optional + + +# 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: + slow = head + fast = head + + while fast is not None and fast.next is not None: + slow = slow.next + fast = fast.next.next + + if fast is slow: + return True + + return False diff --git a/141_Linked_List_Cycle/step3_set.py b/141_Linked_List_Cycle/step3_set.py new file mode 100644 index 0000000..9421e8e --- /dev/null +++ b/141_Linked_List_Cycle/step3_set.py @@ -0,0 +1,23 @@ +from typing import Optional + + +# 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: + current_node = head + visited_nodes = set() + + while current_node is not None: + visited_nodes.add(current_node) + current_node = current_node.next + + if current_node in visited_nodes: + return True + + return False diff --git a/141_Linked_List_Cycle/step4.py b/141_Linked_List_Cycle/step4.py new file mode 100644 index 0000000..287bf72 --- /dev/null +++ b/141_Linked_List_Cycle/step4.py @@ -0,0 +1,29 @@ +from typing import Optional + + +# 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: + # リストを先頭から一つずつ歩いていくやつと1つ飛ばし(2倍速)で歩いていくやつがいて + # 速いやつが遅いやつに追いついたらループしてる + # どこかで None が出てきたらループしてない(そこが終端) + + slow = head + fast = head + + while fast is not None and fast.next is not None: + assert slow is not None + + slow = slow.next + fast = fast.next.next + + if slow == fast: + return True + + return False