-
Notifications
You must be signed in to change notification settings - Fork 0
141. 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,112 @@ | ||
| # 141. Linked List Cycle | ||
| * 問題リンク: https://leetcode.com/problems/linked-list-cycle/ | ||
| * 言語: Python3 | ||
|
|
||
| # Step1 | ||
| ## 方針 | ||
| * 循環しているnodeのポインタ `pos` は引数として与えられない | ||
| * 循環を「既に訪れたnodeの値があるかどうか」で検出する | ||
| * 後述の問題は出ると思ったがとりあえず実装してみた | ||
|
|
||
| ### すべてのテストケースを通過しないコード | ||
| ```python | ||
| class Solution: | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| if not hasattr(head, "next"): | ||
| return False | ||
|
|
||
| current_node = head | ||
| visited_node_val = [] | ||
|
|
||
| while True: | ||
| if current_node.val in visited_node_val: | ||
| return True | ||
| if current_node.next is not None: | ||
| visited_node_val.append(current_node.val) | ||
| current_node = current_node.next | ||
| else: | ||
| return False | ||
| ``` | ||
|
|
||
| * 案の定、循環していなくても同じ値があれば循環と誤検出する | ||
| * 答えを考えて5分過ぎていたので正答を見る | ||
|
|
||
| ### 正答 | ||
| ```python | ||
| class Solution: | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| current_node = head | ||
| visited_node = set() | ||
|
|
||
| while current_node: | ||
| if current_node in visited_node: | ||
| return True | ||
| visited_node.add(current_node) | ||
| current_node = current_node.next | ||
|
|
||
| return False | ||
| ``` | ||
|
|
||
| * そもそもnodeの値(`current_node.val`)ではなく、nodeオブジェクトの参照の値(メモリ上のアドレス)を追加すれば(一意となるので)良い | ||
| * `while True` としない場合、以下は無くても動く | ||
| ```python | ||
| if not hasattr(head, "next"): | ||
| return False | ||
| ``` | ||
| * 時間計算量: $O(n)$ | ||
| * 空間計算量: $O(n)$ | ||
|
|
||
| # Step2 | ||
| ## 別解を読む | ||
| * https://github.com/ryosuketc/leetcode_arai60/pull/1 | ||
| - 一回の走査で2つ先を読む「速いポインタ」と1つ先を読む「遅いポインタ」の2種類のポインタを用意して、その2つのポインタが指す位置が同じになるかどうかで循環検出を行う方法 | ||
| - フロイドの循環検出法と呼ぶらしい | ||
| - アルゴリズム自体は単純だが、すぐに思いつかないアルゴリズムだと思った | ||
| - このアルゴリズムは「常識」の範囲内なのだろうか? | ||
|
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. 範囲外かと思います。 以下引用 いや、正直、私はこの問題はこちらの解答が標準だと思います。 Tortoise and Hare をその場で発見した場合、チューリング賞を取った Floyd 並に賢いです。 上の set を使ったコードが書けた場合、普通はできます。 というわけで、set を使えるかを判定している出題です。 |
||
| - 変数の初期化順序 | ||
| - https://docs.python.org/3/reference/simple_stmts.html#assignment-statements | ||
| - `!=` と `is not` の違い(値の比較かid checkか) | ||
|
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://discord.com/channels/1084280443945353267/1084283898617417748/1387536834468511947 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/3.13/reference/expressions.html#is
https://docs.python.org/3/library/functions.html#id
メモリアドレスを表しているのはあくまでCPythonの実装の話なので、id checkという理解で良い気がします。 |
||
| - https://docs.python.org/3/library/operator.html#mapping-operators-to-functions | ||
| - https://peps.python.org/pep-0008/#programming-recommendations | ||
|
|
||
| * https://github.com/mptommy/coding-practice/pull/1 | ||
| - node数の上限が決まっているので、ループ回数がその上限を超えたら循環とみなす方法 | ||
|
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. 問題の解法としてはあるかなと思うのですが、個人的に実際のソフトウェアの実装では使いたくないなと感じます。 |
||
| - 問題として答えは一応出るが、正規表現のReDoSの検出っぽい方法だと思った | ||
|
|
||
| ## フロイドの循環検出法で解く | ||
| ```python | ||
| class Solution: | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| fast_ptr = head | ||
|
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://google.github.io/styleguide/pyguide.html#316-naming
ただし、上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。 |
||
| slow_ptr = head | ||
|
|
||
| while fast_ptr and fast_ptr.next: | ||
|
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://peps.python.org/pep-0008/#programming-recommendations
|
||
| fast_ptr = fast_ptr.next.next | ||
| slow_ptr = slow_ptr.next | ||
|
|
||
| if fast_ptr == slow_ptr: | ||
| return True | ||
|
|
||
| return False | ||
| ``` | ||
|
|
||
| # Step3 | ||
| ```python | ||
| class Solution: | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| fast_ptr = head | ||
| slow_ptr = head | ||
|
|
||
| while fast_ptr and fast_ptr.next: | ||
| fast_ptr = fast_ptr.next.next | ||
| slow_ptr = slow_ptr.next | ||
|
|
||
| if fast_ptr == slow_ptr: | ||
|
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. ここはどちらかというと identify check なので |
||
| return True | ||
|
|
||
| return False | ||
| ``` | ||
| * 解答時間 | ||
| - 1回目: 1:10 | ||
| - 2回目: 1:10 | ||
| - 3回目: 1:06 | ||
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.
そもそも Python で
hasattrを使うこと自体黒魔術みがあるような気がして、個人的には避けたいのは私だけでしょうか…?一般的な感覚かどうかは自信がないです。ここに限れば
if head is None:のほうがよいかと思います。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.
静的型付けに慣れてるからか同じくhasattr/getattrに苦手意識があります…
これがダックタイピングか!となりました。https://docs.python.org/ja/3/glossary.html#term-duck-typing
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.
正直ここは書いていてもう少し上手いやり方がありそうだとは思っていました。