Skip to content

0141. linked list cycle #1

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
16 changes: 16 additions & 0 deletions 0141. Linked List Cycle/1st_step.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if head is None or head.next is None:
Copy link

Choose a reason for hiding this comment

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

head.next is Noneはなくても大丈夫だと思います

Copy link
Owner Author

Choose a reason for hiding this comment

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

以下のループの箇所でhead.next is Noneの場合ループを抜けるので上記のif文のhead.next is Noneは不要と理解しました。
そういう意味だとhead is Noneも以下の処理に含まれているので不要な気もしてきました。。

while head:

return False
while head:
Copy link

Choose a reason for hiding this comment

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

while head is not None の方がPEP8的にはおすすめのようでした。

None のようなシングルトンと比較をする場合は、常に is か is not を使うべきです。絶対に等値演算子を使わないでください。また、 本当は if x is not None と書いているつもりで、 if x と書いている場合は注意してください - たとえば、デフォルトの値がNoneになる変数や引数に、何かしら別の値が設定されているかどうかをテストする場合です。この「別の値」は、ブール型のコンテクストでは False と評価される(コンテナのような)型かもしれませんよ!

https://pep8-ja.readthedocs.io/ja/latest/#section-36

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、、今の書き方で問題ないですが、型によっては問題が起きうることがあるということですね。。
if x is not Noneは対Noneのみだけで判別できているが、if xだとNone以外も含めFalseになり得るということですね。
対Noneを判別したい場合は確かに前者の方が安全な気がしますね。使い方を注意したいと思います。

if head.val == "*":
return True
head.val = "*"
Copy link

@goto-untrapped goto-untrapped Aug 8, 2024

Choose a reason for hiding this comment

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

入力を破壊して求めるやり方もできますし、入力を破壊しないで求めるやり方もできそうですよね。

Copy link

Choose a reason for hiding this comment

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

"" に特別な意味をつける方法は、""を別の人が別の特別な意味をつけると破綻しますので、デメリットが大きいのでそのデメリットに見合うものでなかったら避けましょう。また、None などのほうが特別の意味をもたせることのデメリットが小さめです。あと、入力を破壊していいかは状況次第ですのでそれも別途考えましょう。

Copy link
Owner Author

@ryo-devz ryo-devz Aug 9, 2024

Choose a reason for hiding this comment

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

ありがとうございます。確かに自分ルールで特別な意味を持たせると、他の人が別の意味を持たせてしまうと破綻するので非推奨に思えますね、、Noneなどの予約語で特別な意味を持たせることはまたメリットデメリットの度合いが異なりそうですね。。新しい観点で勉強になりますm(_ _)m

また、入力を破壊するというところはもう少し意識して実装したいと思います。
入力を破壊した実装をすると以下のようなことが起きてしまうので引き数を破壊せず実装する方がより良いのではないかと個人的には思いました!

  • 引き数が想定外の値に変わってしまうのでその引き数を利用する際に想定外の挙動が起きる
  • 特別な意味を持たせた値があったとして、その値をたまたまもともとList内に持っていた場合に破綻する

head = head.next
Copy link

Choose a reason for hiding this comment

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

head は通常連結リストの先頭のノードを表すそうなので、別の変数名にした方が良いかもしれません。
h1rosaka/arai60#2 (comment)

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。agreeです。headを動かすとheadじゃないやん!ってなりますもんね。。
こういった場合は別の変数名にしようと思います!

return False
17 changes: 17 additions & 0 deletions 0141. Linked List Cycle/2nd_step.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if head is None or head.next is None:
return False
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
Copy link

Choose a reason for hiding this comment

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

ちなみにこの解き方はフロイドのアルゴリズムと呼ばれる手品みたいなものだそうです。

setを使った解き方もありますので、ご参考まで

Copy link
Owner Author

Choose a reason for hiding this comment

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

手品という表現は面白いですね・・・知る人ぞ知るトリックって感じのアルゴリズムってことですね。
必ずしも知ってなくてもよいようなアルゴリズムなのですね。。
setを使った解き方もできるのですね!確認してみます。

Copy link

Choose a reason for hiding this comment

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

私の科学手品という表現の意図は、科学に興味を引かせるために使うもの、くらいのつもりです。

たとえば、こういうやつです。偏光で黒い板があるように見えるというもの。
https://www.sci-museum.jp/study/staff/94/

15 changes: 15 additions & 0 deletions 0141. Linked List Cycle/3rd_step.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False

Choose a reason for hiding this comment

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

汎用的な方法として使えそうという意味合いで、1st_step.py のようなやり方いいですね。

Copy link
Owner Author

@ryo-devz ryo-devz Aug 9, 2024

Choose a reason for hiding this comment

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

ありがとうございます。若干力業感は否めないですが・・かつstep2以降は知ってる人が解けるアルゴリズムのようなのでよりそのように感じるのかも知れないですね。。
今後、汎用的に利用できるかという視点を持って実装したいと思います!

104 changes: 80 additions & 24 deletions 0141. Linked List Cycle/Note.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,107 @@
# 問題文
# 問題概要
問題: 141. Linked List Cycle

https://leetcode.com/problems/linked-list-cycle/description/

言語: Python

# Step1

かかった時間:XXmin
かかった時間:10min

思考ログ:
- 問題を理解する。
- AAA
- BBB
- XXX
- YYY

```python
- Linked Listが与えられて、サイクル(循環)が存在するかを判定する問題。

- どのような解き方ができそうか。
- Linked Listをループさせて、要素に印をつけていき、印のある要素が再度出現した場合はTrueを、ループを抜けてしまった場合はFalseを返す方法でいけそう。

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

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if head is None or head.next is None:
return False
while head:
if head.val == "*":
return True
head.val = "*"
head = head.next
return False

```
疑問点:
- XXX
-
- Linked Listとは?
- ノードの値と次の要素の参照を持っているデータの構造体。配列と比較してデータの挿入と削除が可能。
Copy link

Choose a reason for hiding this comment

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

「配列と比較してデータの挿入と削除が可能。」の部分がよくわからなかったのですが、固定長配列の話をしていますか?確かに固定長配列の場合、サイズを変えようとしたら新たにメモリを確保してそこに要素をコピーする必要があると思うのですが、そういう意味でデータの挿入と削除ができないということでしょうか?

他にも配列とはそもそも何なのか、動的配列、Pythonのリストの内部の仕組み、配列/連結リストの末尾や真ん中への要素の追加・削除の時間計算量などを調べてみるといいと思います

Copy link
Owner Author

Choose a reason for hiding this comment

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

浅い知識で深掘りできてなかったので、あまり深い意味はなくメモ書きしてしまってました。。
もう少し記載いただいた内容のあたりを調べてみようと思います!

- 'pos'とは?
- 循環の始まりのindexを表す。-1の場合、循環しない扱いとなる。
- 引数のLinked Listが書き換わってしまうので、変更せず解く方法はないか。

参考リンク
-
- Linked List
- https://www.geeksforgeeks.org/linked-list-data-structure/

# Step2
かかった時間:XXmin
かかった時間:15min

思考ログ
- XXX

```python

```
- その他の解き方が無いか調べてみる。
- フロイドの循環検出法というもので対応できそう
- 速く動く、遅く動く2つのインデックスを利用して循環を検出するアルゴリズム
- https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%AD%E3%82%A4%E3%83%89%E3%81%AE%E5%BE%AA%E7%92%B0%E6%A4%9C%E5%87%BA%E6%B3%95

- XXX
```python

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

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if head is None or head.next is None:
return False
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
```

# Step3
かかった時間: XXmin
かかった時間: 5min

上記を書き直し、実装
上記を書き直して実装

```python
- 1行目のif文はwhile文の条件と重複しているため破棄。

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

class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
```

# Step 4
- レビューを持って修正を行う
# Step4
- レビューを受けて修正を行う

```python

Expand Down