Skip to content

Create ReverseLinkedCycle.md #8

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 1 commit into
base: main
Choose a base branch
from
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
120 changes: 120 additions & 0 deletions ReverseLinkedCycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# 206.Reverse Linked Listを解きました。レビューの程よろしくお願いします。

## ## 参考にした方々(Pythonで書かれた直近5名)
- https://github.com/olsen-blue/Arai60/pull/7/files
- https://github.com/t0hsumi/leetcode/pull/7/files
- https://github.com/rinost081/LeetCode/pull/8/files
- https://github.com/katataku/leetcode/pull/7/files
- https://github.com/ichika0615/arai60/pull/6/files

## Step 1
### 考えたこと
- Stackの問題らしい。Linked Listを順番に読んでいってstackに格納して、逆順に読み出した値を補完するLinked Listを番兵付きで作れば良さそう
- が、Memory Limit Exceededで動かずギブアップ。下記は動かなかった時のコード。

```Python
# Memory Limit Exceededで動かないコード
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
scan = head
stack = []

while scan:
stack.append(scan)

Choose a reason for hiding this comment

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

私も最初、よく考えずにノードをスタックにそのまま格納して書いて、MLEでした。
ノードの値の情報だけ抽出してスタックに格納(->その後取り出しながらnewノードを編んでいく流れ)する方法にシフトしたら、解決しました。

scan = scan.next

sentinel = ListNode(0, scan)
Copy link

Choose a reason for hiding this comment

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

sentinel.valは使わないので、わざわざ指定せずにsentinel = ListNode(next=scan)でいいんじゃないでしょうか?

https://docs.python.org/3/glossary.html#term-argument

Copy link

Choose a reason for hiding this comment

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

そもそも、この scan は必ず 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.

最後のnodeで止まっているつもりで書いていた様です...(し、その後stack[-1]をnextにしているしでぐちゃぐちゃですね...)。ありがとうございます。

わざわざ指定せずにsentinel = ListNode(next=scan)でいいんじゃないでしょうか?
今度からはこうしてみます、ありがとうございます。

reversed = sentinel

while stack:
reversed.next = stack[-1]
stack.pop()
reversed = reversed.next

return sentinel.next
```
- 他の人の回答をみてると全部のnextの情報を繋ぐのをやめてメモリを節約するとMemoryLimitに収まるらしいので書いてみる
。printしてみるとリストの各要素がnextの果てまで記録していて、空間計算量がO(N^2)になっていた様で、全く気づいてなかった。
Copy link

Choose a reason for hiding this comment

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

これは、二重に間違えています。試しに、

        reversed.next = None
        return sentinel.next

というふうに、return の上に一行足してみましょう。これで動くはずです。

そのうえで、もう一回考察してみて下さい。

Copy link
Owner Author

Choose a reason for hiding this comment

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

試してみたら動きました。なるほど...。

print(reversed.next)してみたところ"Error - Found cycle in the ListNode"が表示されました。while文の中でずっとループを作りながら遡っていたから、そこにトラップされてエラーを吐いていたのですね。

「2重」と仰っていたのはこれに加えて、意図した動きをしていたら空間計算量は普通にO(N)だからですね。
printされて出てくる

ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: ListNode{val: 2, next: ListNode{val: 1, next: None}}}}}

も今見るとリストの大きさ分保存されているだけですね(今見るとむしろ何を見てO(N^2)と思ったのか不思議です...。Memory Limit Exceededという語感から空間計算量に問題があるという先入観を持ってしまったのだと思います。)。

Copy link

Choose a reason for hiding this comment

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

理解を一応確認します。最後に pop してきたものの next がその一つ前のやつになっているので、最後の2要素がループになっています。

Memory Limit Exceeded になるのは、おそらく出力を検証している部分が出力をリストに直してから結果を検証しています。そして、そこが無限ループに陥っています。

ここで得られる教訓は、仕事を引き継ぐときに何を引き継いでいるかを意識しようということ、です。

Copy link
Owner Author

@SanakoMeine SanakoMeine Jan 8, 2025

Choose a reason for hiding this comment

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

最後に pop してきたものの next がその一つ前のやつになっているので、最後の2要素がループになっています

これは理解できておりました。

出力を検証している部分が出力をリストに直してから結果を検証しています。

具体的な検証の仕方のイメージはついておらず、この推測は立っておりませんでした。

ここで得られる教訓は、仕事を引き継ぐときに何を引き継いでいるかを意識しようということ、です。

ノード1つ分を処理し終えた時にどこまで仕事が終わっているか認識が甘かったために、最後の要素が1つ前の要素を指したままでループを形成してしまったことに気づけなかったものと認識しています。その意味でおっしゃる教訓はキャッチできていると考えています。

- reversed = stack[-1]をする必要はなくて、reversed = stack.pop()とするだけで消したvalueを返してくれる

```Python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
scan = head
stack = []

while scan:
stack.append(scan)
scan = scan.next

sentinel = ListNode(0, scan)
reversed = sentinel
Copy link

Choose a reason for hiding this comment

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

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます、last_nodeやprevious_nodeで考えてみます。


while stack:
last_node = stack.pop()
reversed.next = ListNode(last_node.val)
reversed = reversed.next

return sentinel.next
```

## Step 2
### 学んだこと
- List.reverese()で一発で逆順にしてくれる
- Stackを使う方法、再帰を使う方法(https://github.com/goto-untrapped/Arai60/pull/27/files/14646ec0859dd9411e6983bf6c63e6f15a1f9f32#r1638693522)、
切断して繋ぐ方法、後ろから繋いでいく方法があった。後ろから繋いでいく方法が短くて綺麗。こちらの方が一通りやられている: https://github.com/ichika0615/arai60/pull/6/files
Copy link

Choose a reason for hiding this comment

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

Memory Limit Exceeded のヒントはこれです。



### 後ろから繋いでいく方法
```Python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
original_node = head
reversed_node = None

while original_node:
reversed_node = ListNode(original_node.val, reversed_node)
original_node = original_node.next

return reversed_node
```

### 切断して繋ぎ変えていく方法(reversedとforwardの2つのLinked Listを用意して、headから前進させつつその前の値をreversedに格納)
- While内の1番上はtempに入れてるだけ、2行目が繋ぎ変えの操作、下2行は2つのノードの更新処理
```Python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
scan = head
reversed = None

while scan:
scan_next = scan.next
scan.next = reversed
reversed = scan
scan = scan_next

return reversed
```


## Step 3
### コメント
- 切断してから繋ぐやり方の理解が怪しい(過去の問題のPRで指摘され中)のでこれで書いてみる
Copy link

Choose a reason for hiding this comment

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

- 2:30, 1:57, 1:55

```Python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
scan = head
previous_scan = None

while scan:
# scan.nextはtempに同じ
scan_next = scan.next
# 実際の仕事(ListNodeをひっくり返す)
scan.next = previous_scan
# 更新
previous_scan = scan
scan = scan_next

return previous_scan
```