Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions 141. Linked List Cycle/141. Linked List Cycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
問題文: https://leetcode.com/problems/linked-list-cycle/description/

# step1: 何も見ないで考える
- 空間計算量がO(1)になる方法は思いつかなかったので、素直にsetを使った方法で書いた。
- 今回の方法の時間計算量はO(n)、空間計算量はO(n)。
- Noneはシングルトンだから判定に使う演算子は'=='でなく'is'である、という事実だけを特に理解せず記憶していたのでついでに理由を調べてみた。
- 'is' はオブジェクトの同一性を判定するための演算子。
- '==' は値の比較のための演算子で、x == y は x.__eq__(y) という特殊メソッドを呼び出している。
- __eq__() はデフォルトだと is と同じだが、カスタマイズ可能。
- シングルトンが何なのかよく分からなかったが、絶対に1個のインスタンスしか持たないオブジェクトみたいなものだと理解しておく。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

それでいいかと思います。

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のドキュメントで解決しなかったのでwikipediaなどを参考にしました。ただ、ひらがなの読み書きを練習している段階で真剣に取り組むべき話題でもなさそうなのでアドバイス通り先に進むことにします。
https://ja.wikipedia.org/wiki/Singleton_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

- 結局、Noneオブジェクトには絶対に1つしかインスタンスがないんだからオブジェクトの同一性をisで判定すれば十分、ということか。よくわからん。\
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

この問題で==よりisが推奨されることが多いのは、
自分以外の読み手の負荷を下げるためと理解しています。

今回、自作(leetcode運営側の定義ではありますが)のオブジェクトを対象としているので、
==を使う場合、どのような実装になっているかを気にする必要がありますが
isだとそのような気遣いが読み手に不要になります。

今回のような小規模なコードでは大した問題ではないですが、
大規模なコードを多くのメンバで作成していく場合、
一人一人が読み手側の負荷を下げることが重要なのだと理解しています。
(私自身はまだ大規模プロジェクトに携わったことがないのですが)

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://docs.python.org/3.13/library/operator.html \
https://docs.python.org/3.13/reference/datamodel.html#basic-customization
```python
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
checked_nodes = set()
node = head
if node is None:
return False
while node.next is not None:
if node in checked_nodes:
return True
checked_nodes.add(node)
node = node.next
Copy link
Copy Markdown

@Kota-Isayama Kota-Isayama Oct 15, 2025

Choose a reason for hiding this comment

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

while node is not None:
    if node in checked_nodes:
        return True
    checked_nodes.add(node)
    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.

と思いましたが、他の方も指摘いただいているので大丈夫そうですね

return False

```


# step2: 他の人のレビューを参考にコードを整える
## setを使った方法
- setの名前は visited_nodes や visited としている人が多かった。visited系の名前は連結リストの上を移動しているようなイメージがして処理を頭の中で描きやすい気がするので私も採用した。
- 連結リストのnodeを調べるのは分かってるから、setの名前にnodesは入れなくてもいいという意見もあり、もっともなのでこちらも採用。
```python
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
visited = set()
node = head
if node is None:
return False
while node.next is not None:
if node in visited:
return True
visited.add(node)
node = node.next
return False

```

## フロイドの方法
- 新井さんの解説動画で知った。通常人が初見で思いつける方法ではないと思ったのでDiscordの履歴を調べたら「科学手品」みたいなものということだったので一安心。\
https://discord.com/channels/1084280443945353267/1195700948786491403/1195944696665604156
- ループの条件で少し手こずったが、ウサギはノードを1つ飛ばして移動するので到着予定である2つ先のノードが存在してるかまでチェックすると整理した。
```python
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
fast = head
slow = head
if fast is None:
return False
while fast.next is not None and fast.next.next is not None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここもですね、while fast is not None and fast.next is not Noneでなかった理由が気になります👀

fast = fast.next.next
slow = slow.next
if fast is slow:
return True
return False

```


# step3: 10分以内にエラーを出さずに3回書く
## setを使った方法
```python
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
visited = set()
node = head
if node is None:
return False
while node.next is not None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ループの中ではnode.next is not Noneであることを必要としないので単にnode is not Noneで良いかなと思いました。そうすると79-80行目の例外処理も要らなくなります。

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.

確かにおっしゃる通りですね。全然気が付いてなかったです。コードの条件の整理はかなり苦手のようなので改善できるよう精進します。

if node in visited:
return True
visited.add(node)
node = node.next
return False

```

## フロイドの方法
```python
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
fast = head
slow = head
if fast is None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if fast is None: の部分は、
fastを代入前の引数名headにして、
この行の場所もこの関数内の最初の行に移動すると、
より直接的に、引数headの不備で条件分岐するということがわかるため、読み手への負荷が下がるように思いました。

def hasCycle(self, head: Optional[ListNode]) -> bool:
   if head is None:
       return False
   fast = head
   slow = head

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.

確かにおっしゃる通りですね。指摘されるまで気づいていなかったので、コードの整理はかなり課題がありそうです。意識的に改善できるよう精進します。

return False
while fast.next is not None and fast.next.next is not None:
fast = fast.next.next
slow = slow.next
if fast is slow:
return True
return False

```

次の問題: 142. Linked List Cycle II ( https://leetcode.com/problems/linked-list-cycle-ii/description/ )