Solved 142_Linked_List_Cycle_2#2
Conversation
| def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
| slow = head | ||
| fast = head | ||
| while fast and fast.next: |
There was a problem hiding this comment.
ファイル上部に記載されたコメントを踏まえると while fast is not None and fast.next is not None: と書くこともありだと思います。
There was a problem hiding this comment.
厳密に(保守的に)書くと読みづらいかと思い、可読性を優先して簡潔に書きましたが、コード内で一貫性があったほうがよいのは確実ですね。
レビューありがとうございます。
| fast = fast.next.next | ||
| if slow is fast: | ||
| fast = head | ||
| while fast is not slow: |
There was a problem hiding this comment.
個人的にはネストが深いなと思いました。コードの整え方や他の人が書いたコードを見た上でしっくりくる形に落ち着いているのであれば良いと思います。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.9kpbwslvv3yv
There was a problem hiding this comment.
私もネストが深いのが少し気になりました.
slowとfastがぶつかる地点を求めるロジックを実装した関数を用意するのはどうでしょう?
以下,他の方の実装例です.
https://hayapenguin.com/notes/LeetCode/142/LinkedListCycleTwo
There was a problem hiding this comment.
お二方、丁寧に参考資料までありがとうございます。
そこまで考えが及んでいなかったので、非常に勉強になりました。
今後は「構造的可読性」に配慮して、複数の選択肢の中から書き方を決めたいと思います。
特に、while ~ else は書いたことがなかったので、書いてみました。
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
break
else:
return None # -> no cycle found
fast = head
while fast is not slow:
slow = slow.next
fast = fast.next
return fastThere was a problem hiding this comment.
そうですね。必ずどれを使うべきというのはないのですが、頭の中でこういう変形をたくさんしてしっくりくるものを探しています。
There was a problem hiding this comment.
思考の流れをコードに落としこむだけでなく、その書き方・構造を初見の人が一読で理解できるかという観点が重要だと受け取りました。訓練で身につけたいと思います。
| return current | ||
| visited.add(current) | ||
| current = current.next | ||
| return None |
There was a problem hiding this comment.
とても読みやすいと思います!
ネストを浅くする方法として,
# currentが末尾に達するか,
# visitedなノードに達するまでwhile
while current is not None and current not in visited:
visited.add(current)
current = current.next
return currentとすることが考えられますが,どちらが読みやすいかは人による気がしますね.
There was a problem hiding this comment.
ありがとうございます!
どの深さのネストまで許容するか、チーム内でルールを決めておくと、バランスの取れた選択ができそうです。
| fast = fast.next.next | ||
| if slow is fast: | ||
| fast = head | ||
| while fast is not slow: |
There was a problem hiding this comment.
私もネストが深いのが少し気になりました.
slowとfastがぶつかる地点を求めるロジックを実装した関数を用意するのはどうでしょう?
以下,他の方の実装例です.
https://hayapenguin.com/notes/LeetCode/142/LinkedListCycleTwo
| fast = head | ||
| while fast is not slow: | ||
| slow = slow.next | ||
| fast = fast.next |
There was a problem hiding this comment.
こちら,fast変数が再度使用されて,
fast = headとしたあと,今度は1つづつ進んでいる (fastに進んでいない) のが気になりました.
別の変数を用意してはどうでしょうか (上のリンクの方の実装だと,from_start).
There was a problem hiding this comment.
ご指摘通り、
- 連結リストのサイクル検知
- サイクル開始点の探索
は(繋がっているが)別の処理なので、from_start などの別の参照だと意味的に分かる変数名にすべきだと思いました。ありがとうございます。
| fast = fast.next.next | ||
| if slow is fast: | ||
| fast = head | ||
| while fast is not slow: |
There was a problem hiding this comment.
そうですね。必ずどれを使うべきというのはないのですが、頭の中でこういう変形をたくさんしてしっくりくるものを探しています。
| - 両辺 mod l_cycle を取ると、d (mod l_cycle) = (- l_in) (mod l_cycle) | ||
| - これより、合流地点から距離 l_in 進めば、サイクルの入口に達することが分かる。 | ||
| - ただ、具体的な l_in は分からない(それを求めるのが題意なので)。 | ||
| - だが偶然にも、l_in は head からサイクル入口までの距離なので、片方のポインタを head に戻せば、合流地点からスタートするポインタ A と head から移動するポインタ B を同じ速度で動かせば、ちょうどサイクル入口で合流することになる。 |
There was a problem hiding this comment.
There was a problem hiding this comment.
ありがとうございます。拝読しました。
ところで、この戻っていく時、うさぎとかめは、衝突点から同じ速さで歩いて戻っているので、合流点までは一緒にいましたね。
サイクル開始点を求めるテクニック(処理)は、数式を使わないと自分は納得できないと思っていましたが、この説明で腑に落ちました。
This problem
-> 142. Linked List Cycle II
Next
-> 83. Remove Duplicates from Sorted List
Language
-> Python3