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
160 changes: 160 additions & 0 deletions 142.Linked_List_Cycle_II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
1st.
方針として141.Linked List Cycleと同様にsetで訪れたnodeを保持し、訪れていた場合そのnodeを返す。訪れていない場合はsetに追加しnodeを次に進めるという操作をnodeがNoneになるまで繰り返すという方針を思いつき実装した。
141を解いてから日が空いていないないためかスムーズに書くことが出来た。
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
visited = set()
node = head
while node:
if node in visited:
return node
visited.add(node)
node = node.next
return None
```

2nd.
1st.の段階でフロイドの循環検知でも解けるか考えたが、循環していることは検知できても、fastがslowに追い付いたnodeがCycleの始まりとなっているnodeとは限らないためどうしようか迷い思いつかなかったため他の方の回答を見て実装した。
(https://discord.com/channels/1084280443945353267/1246383603122966570/1252209488815984710)
を見て理解は出来たがこれを思いつくのは厳しそう
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head

while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
break
else:
return 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-else は Python に特徴的な文法です。
整え方にいくつか方法があるので、このあたりを見ておいてください。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.4qnnfvpo8ij5

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 = 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.

この段階でfastという変数の意味が変わっています。アルゴリズムを知っている人からすると自明ですが、初見だと戸惑うかもしれません。

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を使っていましたが確かに分かりずらさもあるかもしれません。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私も以前同様のコメントをいただきました。
今であれば、fastではなく、originなどという変数名に置き直す気がします。

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.

コメントありがとうございます。
確かにそれはよい選択肢ですね。
参考にさせていただきます。

while fast != slow:
fast = fast.next
slow = slow.next
return fast
```
始めは下のように書いていた。
```Python
while fast and fast.next:
fast = fast.next.next
slow = slow.next

if fast == slow:
fast = head
while fast is not slow:
fast = fast.next
slow = slow.next
return fast

return None
```
しかし、whileの中のifが長くなるが見づらくなるので外に出すようにした(https://discord.com/channels/1084280443945353267/1221030192609493053/1225674901445283860)
while - else を使うのがしっくりきた。

Discordを見ていくとsetを用いる方法の亜種といえると思うが再帰を使う方法もあると知った。

その中で(https://github.com/h1rosaka/arai60/pull/4/files#diff-97a2b5510d65cd5c736cab9a3675eb2122bfe5e9ccbe491032aad6fee9d3f74d)での話は大変参考になった。

再帰の場合はstack overflowに注意する必要がある。これは無限再帰などでstackにdataをpushし続けることでおきる。
* Pythonの場合は再帰呼び出しの上限回数がsys.getrecursionlimit()で確認出来る。環境依存だがデフォルトでは1000回のようで手元で試しても1000であった。(https://docs.python.org/ja/3/library/sys.html#sys.getrecursionlimit)
* setrecursionlimit()で変更もできるが大きくしすぎるとクラッシュする恐れがある。
* C/C++でも大きくて10M程度であるようだ

今回の例で言えばn個のnodeがcycleを持たずに並んでいた場合再帰はn回呼び出されstackにpushされることになるので入力nodeの大きさが大きくなるとstack over flowの恐れがあると考えられる。(LeetCodeのコードが実行される環境での再帰呼び出し回数の上限は分からない)

また,クイックソートで再帰を用いる場合、stack over flow対策として再帰は短い方からやる。長い方では末尾再帰最適化を行うなどの工夫があるよう。(https://nuc.hatenadiary.org/entry/2021/03/31#:~:text=%E7%9F%AD%E3%81%84%E6%96%B9%E3%81%8B%E3%82%89%E5%86%8D%E5%B8%B0%E3%81%97%E3%81%A6%E3%80%81%E9%95%B7%E3%81%84%E6%96%B9%E3%81%AF%E6%9C%AB%E5%B0%BE%E5%86%8D%E5%B8%B0%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8)

```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
visited = set()

def _check_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.

inner function の関数名の先頭には _ を付けなくてよいと思います。非 public な関数名には付けたほうがよいと思います。

https://peps.python.org/pep-0008/#method-names-and-instance-variables

Use one leading underscore only for non-public methods and instance variables.

https://google.github.io/styleguide/pyguide.html#3162-naming-conventions

Prepending a single underscore (_) has some support for protecting module variables and functions (linters will flag protected member access).

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 is None:
return None
if node in visited:
return node

visited.add(node)

return _check_node(node.next)

return _check_node(head)
```

3rd.
set()とtwo pointer no2通りの方法について3回書けるまで行った。
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
visited = set()
node = head

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

return None
```

```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head

while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
break
else:
return None

# breakした場合
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return fast
```