Skip to content

142. Linked List Cycle II - #2

Open
kazizi55 wants to merge 3 commits into
mainfrom
142-linked-list-cycle-ii
Open

142. Linked List Cycle II#2
kazizi55 wants to merge 3 commits into
mainfrom
142-linked-list-cycle-ii

Conversation

@kazizi55

@kazizi55 kazizi55 commented Aug 19, 2025

Copy link
Copy Markdown
Owner

@kazizi55
kazizi55 marked this pull request as ready for review August 19, 2025 23:39

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

よく書けていると思うんですが、全体的に空行が多いような気はします。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.59zydabj6a98

これは減らそうと思って減らすというよりは、自然と減る、という方が感覚として近いのですが。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

おっしゃる通り、他言語とは違ってネストである程度各節の分離を表現できるので、見返してみると冗長に感じる部分がありますね。

全体的に改行をなくしてみました。
https://github.com/kazizi55/coding-challenges/pull/2/files#diff-d7b00169799753b697b7185c622e016e009ca1ca042509631517f45196a88e95R1-R46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改行が減っているこちらの方が読みやすいですね。

finder = finder.next
catchup_node = catchup_node.next

return finder No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かいですが、ファイルの最後には newline があったようがよいですかね (GitHub のプレビューでノイズになるとか、https://stackoverflow.com/questions/729692/why-should-text-files-end-with-a-newline とかの議論があると思います)。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにそうですね。盲点でした、ありがとうございます。
全体的に new line を追加しました!
加えて、vscode で save 時に自動で new line が追加されるようにしてみました。
https://stackoverflow.com/questions/44704968/visual-studio-code-insert-newline-at-the-end-of-files


return None

catchup_node = find_catchup_node(head)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

個人的には catchup_node finder という命名はわかりにくく感じました。catchup だと追いつく、という意味合いですが、実際は、catchup の方が先行する node ではありませんか。

その点では leading_nodefollowing_node というほうがしっくりきます。

一例ですが、私なら関数名も変更してしまって

node_from_intersection = find_intersection(head)
node_from_head = head

といった感じにするかなあ、と思いました。catchup という言葉と実際の処理の認識にずれがある気がします。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

おっしゃる通りですね。ありがとうございます。
fast & slow の概念もこのステップでは捨象されているので、追いつく・追いつかれるという表現自体が合わないなと改めて考えてみて思いました。なので lead & follow よりも intersection の変数名の方がしっくりきました。

intersection を使う形で全体的に書き換えました。
https://github.com/kazizi55/coding-challenges/pull/2/files#diff-d7b00169799753b697b7185c622e016e009ca1ca042509631517f45196a88e95R27-R46


関数化しない場合、2 つ目の while で fast, slow を使い回すのではなくて、origin という新しい変数に代入することもできる。が、origin というと個人的に何かの起源を思い浮かべるのと、origin なのは最初だけでどんどん探査していくことから、finder を使うのが適切だと判断した。
https://github.com/olsen-blue/Arai60/pull/2/files#diff-2d9cbcab205d34e01955561fc8d084614d1d817a33b81d59bdbf2096fb2bd565R158-R163

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

見てそうですがこのあたりにまとめてあるコードの整え方にざっと目を通しておくといいでしょう。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.9kpbwslvv3yv

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

共有ありがとうございます。

https://discord.com/channels/1084280443945353267/1192728121644945439/1194296454181826590

いろいろ自明な書き換えってあるはずで、その中のどれを「技術的に選択」するかという問題です。
各条件や変数、処理の関係をいかにわかりやすく提示するかを技術的に選択するということですね。勉強になります。

https://discord.com/channels/1084280443945353267/1221030192609493053/1225674901445283860

X,Y,Zと処理があったときにそれぞれがどれくらい複雑か、関連しあっているかで1-4のパターンから技術的に選択できるかということですね。


#### Hare and Tortoise

平均 2 分で 3 回とも AC。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AC がゴールからは程遠いという感覚はもってください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい、肝に銘じます。
加えて、Step3 に取り組んだ時点でACしていることは自明なので、次回以降は書かないようにします。

@@ -0,0 +1,65 @@
class SetSolutionWithRecursion:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
visited = set()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recursion、関数の返り値にset()を置くケースを見たことがあるんですが、やっぱり最初にvisited = set()を定義してくれる方が個人的に嬉しいですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recursion、関数の返り値にset()を置くケースを見たことがあるんですが

こんな感じで書けますね。盲点でした。ありがとうございます!

class SetSolutionWithArgument:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        def find_node(node, visited):
            if node is None:
                return None
            if node in visited:
                return node

            visited.add(node)

            return find_node(node.next, visited)
        return find_node(head, set())

やっぱり最初にvisited = set()を定義してくれる方が個人的に嬉しいですね。

同感です。何に格納されるのかが最初に提示されていた方がわかりやすいし自然な感覚があります。

while True という形で無限ループを使うことで None を早期 return できる。
https://github.com/olsen-blue/Arai60/pull/2/files#diff-2d9cbcab205d34e01955561fc8d084614d1d817a33b81d59bdbf2096fb2bd565R149-R156

関数化しない場合、2 つ目の while で fast, slow を使い回すのではなくて、origin という新しい変数に代入することもできる。が、origin というと個人的に何かの起源を思い浮かべるのと、origin なのは最初だけでどんどん探査していくことから、finder を使うのが適切だと判断した。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originは同様に違和感を持ちました。

@brood0783

Copy link
Copy Markdown

Step2の解答比較の時点でかなりフィードバックがかかっているので、見習いたいなと思いました。書いてあるコードにほぼ文句がないです。かなり良いと思いました。

finder = finder.next
catchup_node = catchup_node.next

return finder No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(重複があったので削除したら、2つとも消えたので再掲します)
3stepsが終わったらコンパイラーだけではなく、最後にフォーマッターが何を指摘してくるか予想できるようになると良いと思います。
条件分岐ごとの空行検出は定かではないですが、最後の改行などに関しては検出されるはずです(規約によるかもですが)。

mura0086/arai60#21 (comment)
https://discord.com/channels/1084280443945353267/1196498607977799853/1356188787377700905

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

条件分岐ごとの空行検出は定かではないですが、最後の改行などに関しては検出されるはずです(規約によるかもですが)。

Black, Ruff, autopep8 をとりあえず試してみましたが、おっしゃる通り、最後の改行は検出されました。
条件分岐ごとの空行検出は少なくともデフォルトだと特に指摘されなかったですね。
doc を一通り読んでみましたが、条件分岐ごとの空行検出はなさそうでした。
https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html
https://docs.astral.sh/ruff/rules/
https://pypi.org/project/autopep8/

pep8にもあるように、top-level function や class definition など以外は個人の裁量に委ねられているようですね。

Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.

https://peps.python.org/pep-0008/#blank-lines


class HareAndTortoiseSolutionWithFunction:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
def get_catchup_node(node):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_catchup_node の意味するところは「fast がslow に catch up したnode を返す」だと思うんですが、初見でそのように読み取れませんでした。
それと、get で始まるメソッドはメンバ変数を返すだけの軽量なアクセサであるという印象を与えるため、重い計算をするメソッドには不適であるという意見もあります。

“多くのプログラマは、getで始まるメソッドはメンバの値を返すだけの「軽量アクセサ」であるという規約に慣れ親しんでいる。この規約を守らなければ、誤解を招く可能性がある。”
リーダブルコード 3章 誤解されない名前

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かに関数名からは読み取りきれないですね。ありがとうございます。

get で始まるメソッドはメンバ変数を返すだけの軽量なアクセサであるという印象を与える

Java などだと getter を定義するのに get の prefix を使うので、確かにその印象は受けますね。

find_intersection という名前に変えてみました。
https://github.com/kazizi55/coding-challenges/pull/2/files#diff-d7b00169799753b697b7185c622e016e009ca1ca042509631517f45196a88e95R27-R28

return node

visited.add(node)
return check_node(node.next)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

python は末尾再帰最適化を実装していないようなので、データサイズによってはstack overflow になってしまうかもしれません。

https://stackoverflow.com/questions/13591970/does-python-optimize-tail-recursion

Does Python optimize tail recursion?
No, and it never will since Guido van Rossum prefers to be able to have proper tracebacks

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

python は末尾再帰最適化を実装していないようなので

知らなかったです。ありがとうございます!!
実際に試してみましたが、今回の例だと1497個目くらいから実際に maximum recursion depth exceededのエラーが出ました。とするとやはり中〜大規模な処理をさせるのに少なくともPythonにおいては末尾再帰を使った手法は使わない方が良さそうですね。

Testing Set with Recursion...
Error in test case 1497: maximum recursion depth exceeded
Description: Large list, cycle at beginning
Error in test case 1498: maximum recursion depth exceeded
Description: Large list, cycle at end
Error in test case 1499: maximum recursion depth exceeded
Description: Large list, cycle in middle
Execution time: 0.4523 seconds

No, and it never will since Guido van Rossum prefers to be able to have proper tracebacks:

あとこれも勉強になりました。
デバッグの際にスタックトレースが分かりにくくなることを避けたかったのですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants