-
Notifications
You must be signed in to change notification settings - Fork 0
Create 142. Linked List Cycle II.md #2
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,134 @@ | ||
| 問題文: https://leetcode.com/problems/linked-list-cycle-ii/ | ||
|
|
||
| # step1: 何も見ないで書く | ||
| ## setを使った方法 | ||
| - 時間計算量: O(n), 空間計算量 O(n)。 | ||
| - 前回もらったコメントを参考に条件を整えた。 | ||
| - ループ条件は ```while node:```でもいいが、個人的に読みにくい気がしたので```is not```で判定した。 | ||
| ```python | ||
| 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 | ||
|
|
||
| ``` | ||
|
|
||
| ## フロイドの方法 | ||
| - 時間計算量: O(n), 空間計算量 O(1)。 | ||
| - 自力では思いつかなかったので、新井さんの解説を見た。 | ||
| - ループがネストされて読みにくいが、読みやすくする方法をすぐには思いつかなかったのでとりあえずこれで提出。 | ||
| ```python | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| if head is None: | ||
| return None | ||
| fast = head | ||
| slow = head | ||
| while fast.next is not None and fast.next.next is not None: | ||
| fast = fast.next.next | ||
| slow = slow.next | ||
| if fast is slow: | ||
| slow = head | ||
| while fast is not slow: | ||
| fast = fast.next | ||
| slow = slow.next | ||
| return slow | ||
| return None | ||
|
|
||
| ``` | ||
|
|
||
| # step2: 他の人のレビューを見てコードを整える | ||
| ## setを使った方法 | ||
| - 他の人は```set```オブジェクトの中身まで気にしていたが、自分は見落としていたのでドキュメントを調べた。 | ||
| - set型はハッシュ値を持つオブジェクトを格納するためのオブジェクトで、メンバーの判定```x in set```はハッシュ値を使ってオブジェクトの同一性をチェックしている。\ | ||
|
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. めっちゃ細かいのですが、ハッシュでは同一性ではなく等価性をチェックしている、という方が一般的な気がします。 |
||
| https://docs.python.org/ja/3/library/stdtypes.html#set-types-set-frozenset | ||
| - したがって、```node in visited```でメンバーと判定された```node```を返せば、サイクルの始点と同一のオブジェクトが返されることが保証されている。 | ||
| ```python | ||
| 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 | ||
|
|
||
| ``` | ||
|
|
||
| ## フロイドの方法 | ||
| - サイクルの検知とサイクルの始点の検出を分けると良いというコメントを参考に整えた。 | ||
| - 最初はフラグ変数で実装していたが、小田さんのコメントで構造化が不十分と指摘されていた。そのため、典型コメント集の「コードの整え方」を参考に```while else```構文で実装した。 | ||
|
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. 次に続くものなどとの関係で他のものを使うこともあるので、解き進めながら時々思い出してください。 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. すでにこちらにコメントをした通りですが、 while else 構文は避けたほうが無難だと思います。 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. フラグよりはましとは思いますが関数化するほうがいいでしょう。 |
||
| - ループのネストをやめたので始点検出のための2つめのループで新しい変数名を使いたくなる気持ちになりやすくなった気がする。 | ||
| - step1のコードの読みにくさはループのネストだけでなく変数名の流用も影響していたと気が付いた。 | ||
| - 2つめのループでウサギ役の変数にどのような名前を付けるか迷ったが、無難にfrom_meetingとした。from_collisionにしようかとも思ったがネットワークの文脈で出てくる専門用語と名前がかぶって紛らわしいのでやめた。 | ||
|
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_meetingという変数名はわかりやすいと思いました! |
||
| ```python | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| if head is None: | ||
| return None | ||
| fast = head | ||
| slow = head | ||
| while fast.next is not None and fast.next.next is not None: | ||
|
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.
|
||
| fast = fast.next.next | ||
| slow = slow.next | ||
| if fast is slow: | ||
| break | ||
| else: | ||
| return None | ||
| from_start = head | ||
| from_meeting = fast | ||
| while from_start is not from_meeting: | ||
| from_start = from_start.next | ||
| from_meeting = from_meeting.next | ||
| return from_start | ||
|
|
||
| ``` | ||
|
|
||
| # step3: 10分以内にミスなく3回書く | ||
| ## setを使った方法 | ||
| ```python | ||
| 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 | ||
|
|
||
| ``` | ||
|
|
||
| ## フロイドの方法 | ||
| ```python | ||
| class Solution: | ||
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| if head is None: | ||
| return None | ||
| fast = head | ||
| slow = head | ||
| while fast.next is not None and fast.next.next is not None: | ||
|
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. この部分ですが、 上記とは別の観点のコメントとなりますが、
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. whileの条件については特に理由はなく、完全に私が見落としてただけです。 |
||
| fast = fast.next.next | ||
| slow = slow.next | ||
| if fast is slow: | ||
| break | ||
| else: | ||
| return None | ||
| from_start = head | ||
| from_meeting = fast | ||
| while from_start is not from_meeting: | ||
|
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. この分量だと迷うところですが「循環を検知する」や「循環の始点を検知する」をそれぞれ関数化してみてもいいかもしれません。
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. ありがとうございます!step4で実装してみます! |
||
| from_start = from_start.next | ||
| from_meeting = from_meeting.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.
とても良いと思います。
今回の問題の入力の制約からしてありえないということは抜きにすると、たとえばnodeが空リストだったりしても
while nodeはFalse判定されてしまいます。これが意図せぬ挙動を引き起こすこともあるため、Noneの確認をするときにはis not Noneと書いた方が安全だと思います