-
Notifications
You must be signed in to change notification settings - Fork 0
142. Linked List Cycle Ⅱ #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| ## Step1 | ||
|
|
||
| Linked List Cycle 1と同じようにsetを利用して解く. | ||
| ```py | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| visited = set() | ||
| node = head | ||
| while node is not None: | ||
| if node in visited: | ||
| return node | ||
|
|
||
| visited.add(node) | ||
| node = node.next | ||
|
|
||
| return None | ||
| ``` | ||
|
|
||
| Floydの解法で解いてみる. | ||
| Linked List Cycle 1と異なり, サイクルを検出するだけでなく入口を探さないといけないので入口検出のループがある.(追いついた地点は入口とは限らないため) | ||
| 入口検出では, slow, fastポインタを同じ速度で動かし続ければいつかサイクルの入口でぶつかる. | ||
| 時間計算量: O(N). | ||
| 入口検出ステップで, サイクルを何周もしても計算量は増えない? | ||
| ```py | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| fast = head | ||
| slow = head | ||
| while fast and fast.next: | ||
| slow = slow.next | ||
| fast = fast.next.next | ||
| if fast == slow: | ||
| break | ||
|
|
||
| else: return None | ||
|
|
||
| fast = head | ||
| while fast != slow: | ||
| fast = fast.next | ||
| slow = slow.next | ||
|
|
||
| return slow | ||
| ``` | ||
|
|
||
|
|
||
| ## Step2 | ||
|
|
||
| Floydの解法を綺麗にする. | ||
| https://github.com/docto-rin/leetcode/pull/2/ | ||
| https://github.com/Kaichi-Irie/leetcode-python/pull/20#discussion_r2341229316 | ||
| 変数名などを参考にした. | ||
|
|
||
| また、以下でサイクルを検出する部分を関数化することが提案されていたが、以下のようなメリットがあると思われる. | ||
| - サイクルがあるかどうかのフラグや`while else`構文を回避できる. 前者は制御の流れをわかりにくくし, `while else`構文は | ||
| あまりつかったことがない. | ||
| - サイクルが存在すれば衝突点を返し, 存在しなければNoneを返すシンプルな実装にできる. | ||
|
|
||
| https://github.com/nanae772/leetcode-arai60/pull/3#discussion_r2317374235 | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| def find_intersection(node): | ||
| slow = head | ||
| fast = head | ||
|
|
||
| while fast is not None and fast.next is not None: | ||
| slow = slow.next | ||
| fast = fast.next.next | ||
|
|
||
| # デフォルトでは, __eq__メソッドではオブジェクトの等価性を見ているので `==` で記述しても同じだが以下のように記述するほうが厳密. `is`はポインタの比較? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 以下のようにドキュメントに記載があり、id()はオブジェクトのメモリアドレスを取得するとあります。ポインタが指すメモリアドレスの同一性チェックという意味で大丈夫そうです。
https://docs.python.org/ja/3/reference/expressions.html#is-not
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 補足ありがとうございます! |
||
| if slow is fast: | ||
| return slow | ||
|
|
||
| return None | ||
|
|
||
| intersection = find_intersection(head) | ||
| if intersection is None: | ||
| return None | ||
|
|
||
| from_start = head | ||
| from_intersection = intersection | ||
| while from_start is not from_intersection: | ||
| from_start = from_start.next | ||
| from_intersection = from_intersection.next | ||
|
|
||
| return from_start | ||
| ``` | ||
|
|
||
|
|
||
| ## Step3 | ||
| ```py | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| def find_intersection(node): | ||
| fast = node | ||
| slow = node | ||
| while fast is not None and fast.next is not None: | ||
| slow = slow.next | ||
| fast = fast.next.next | ||
| if fast is slow: | ||
| return slow | ||
|
|
||
| return None | ||
|
|
||
| intersection = find_intersection(head) | ||
| if intersection is None: | ||
| return None | ||
|
|
||
| from_start = head | ||
| from_intersection = intersection | ||
|
Comment on lines
+110
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from_start, from_intersection の変数名分かりやすかったです。 |
||
| while from_start is not from_intersection: | ||
| from_start = from_start.next | ||
| from_intersection = from_intersection.next | ||
|
|
||
| return from_start | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ここのみ違っていたため直し忘れかと思いますが、今回は同一性を確かめる必要があるため、
is notが適切のように思います。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
本当ですね!ありがとうございます