Skip to content
Merged
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
222 changes: 222 additions & 0 deletions Stack/206/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
## 取り組み方
step1: 5分考えて分からなかったら答えを見る。答えを理解したら、答えを隠して書く。筆が進まず5分立ったら答えを見る。答えを送信して正解するまで。
step2: コードを読みやすく整える。動くコードになったら終了。
step3: 時間を計りながら書く。10分以内に3回連続でアクセプトされるまで。

## step1
与えられた連結リストをたどって、新しい連結リストを作ればよさそう。
5 -> 4 -> 3 -> 2 -> 1
を作る場合、
ListNode(1, None)
ListNode(2, ListNode(1, None))
...
と作っていくので、与えられた連結リストを順番通りにたどりながら逆順にした連結リストを作れる。

再帰でもできそうだが、スタックオーバーフローしてしまうので避ける(ノード数が5000までだが、プラットフォームによるがデフォルトだと最大で1000なので)。
Copy link

@huyfififi huyfififi Apr 29, 2025

Choose a reason for hiding this comment

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

確かに、言語自体はデフォルトを定めていませんが、CPythonもPyPyもデフォルトの再帰のlimitは1000っぽいですね(実装によって再帰の深さの意味が若干違うようですが...🤔)。あんまり深く考えたことがなかったので勉強になります!

CPython
PyPy

Copy link
Owner Author

Choose a reason for hiding this comment

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

PyPyのほうは実装確認できてなかったです、ありがとうございます!



```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
ans = None
while node is not None:
ans = ListNode(node.val, ans)
node = node.next
return ans
```

## step2
### 他の人のPRを見る
https://github.com/Satorien/LeetCode/pull/7/files
付け替える方式。
https://github.com/Satorien/LeetCode/pull/7/files#r2055662397
この方式が想定解なんじゃないかという意見

1 -> 2 -> 3 -> 4 -> 5 -> None
があったとしたら、
None <- 1 2 -> 3 -> 4 -> 5 -> None
None <- 1 <- 2 3 -> 4 -> 5 -> None
...
None <- 1 <- 2 <- 3 <- 4 <- 5
のように逆向きにしていく方式

実装してみた。
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
reversed_list = None
while node is not None:
next_node = node.next
node.next = reversed_list
reversed_list = node
node = next_node
return reversed_list
```


再帰でもできるっぽい。まず、自分で考えてみる。
1 -> 2 -> 3 -> 4 -> 5 を考える。
reverseListに1が渡った場合、処理中で再帰的に呼び出したreverseListが5 -> 4 -> 3 -> 2を返せば最後のノードのnextを1にすればいいんだけど、それは筋が悪そう。再帰的に呼び出したreverseListの末尾のノードに1をつけるのはできるが、返ってきたノード数分の走査が必要になるため。

これ最初にスタックか何かに詰める必要がある?
そしたら
(1, None)
(2, (1, None))
...
と順番に作っていけそう。

https://github.com/Satorien/LeetCode/pull/7/files
と思ったけど付け替え + 再帰で出来るみたい。
1 -> 2 -> 3 -> 4 -> 5 -> None
だとして、
1 -> 2 -> 3 -> 4 <- 5
1 -> 2 -> 3 <- 4 <- 5
...
None <- 1 <- 2 -> 3 <- 4 <- 5
になる。


実装してみた。
最後のノードを返し、再帰的に付け替えを行う
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head

node = head
last_node = self.reverseList(head.next)
node.next.next = node
# これがないと1が2を向いたままになってしまう
node.next = None
return last_node
```

## step3
3パターンで実装した。
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.

ですね
・新しく作る方式: 現在までのノードで構成されていて、かつ逆順になった新しいListNodeを次に回す
・付け替え方式: 現在までのノードが逆順になった状態で次に回す
・再帰付け替え方式: 現在のノード以降が逆順になった状態で次に回す
だと理解してます

新しく作る方式だと1通りで、付け替えだと前後ろの2通りあります

どれもノード数をNとした場合に
時間計算量:O(N)
空間計算量:O(N)

ノード数的に再帰の深さを心配しなくてよいのであれば再帰付け替え方式が分かりやすいと思う。ただデフォルト値は1000なので心もとない気がする。。
ノード数的にスタックオーバーフローに懸念がある場合は、新しく作る方式が分かりやすいと思う。

### 新しく作る方式
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
reversed_list = None
node = head
while node is not None:
reversed_list = ListNode(node.val, reversed_list)
node = node.next
return reversed_list
```

### 付け替え方式
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
reversed_list = None
Copy link

@huyfififi huyfififi Apr 29, 2025

Choose a reason for hiding this comment

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

nit この変数が保持するのは厳密には(?)なんらかのlistではなくnodeなので、reversed_headのような名付けの方が齟齬がないかと思いました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ここ少し迷ったんですがその方がいいですね

迷った点としてはメソッド名がreverseListなので、変数も合わせた方が良いのかなと思いましたが、やっぱりListと混同しそうなのでreversed_headが良いと思いました

node = head
while node is not None:
next_node = node.next
node.next = reversed_list
reversed_list = node
node = next_node
return reversed_list

Choose a reason for hiding this comment

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

繋ぎかえで書くなら、単にnodeと書くより、relinkingなどと命名した方が意図が伝わりやすいと思います。

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        relinking = head
        relinked = None
        while relinking is not None:
            next_relinking = relinking.next
            relinking.next = relinked
            relinked = relinking
            relinking = next_relinking

        return relinked

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、relinkをベースとした命名もありですね
下記の役割だと思うので、relinkedはlast_relinkedが良いと思いましたがいかがでしょう?

  • 次に繋ぎ変えるノード: next_relinking
  • 繋ぎ変えているノード: relinking
  • 繋ぎ変えたノードの一番最後: last_relinked

Copy link
Owner Author

Choose a reason for hiding this comment

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

コメントを踏まえて実装しました
dc739b0

```

### 再帰的に付け替える方式
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head

node = head

Choose a reason for hiding this comment

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

再帰的に問題を分割していて、部分問題で見たらずっとheadに対して操作をしているので、nodeという別の名前を与えなくてもいいと思います。

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)に対して操作しているのでnodeという別の名前は不要だと思いました

Copy link
Owner Author

Choose a reason for hiding this comment

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

コメントを踏まえて実装しました
395d517

last_node = self.reverseList(node.next)
node.next.next = node
node.next = None
return last_node
```

## レビューコメントを反映した実装
### 繋ぎ変え
修正点
- last_relinked, relinking, next_relinking という変数名に修正した
-

```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
relinking = head
last_relinked = None

while relinking is not None:
next_relinking = relinking.next
relinking.next = last_relinked
last_relinked = relinking
relinking = next_relinking

return last_relinked
```

### 再帰繋ぎ変え
修正点
- headをそのまま利用
- reversed_headという変数名に修正

```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head

reversed_head = self.reverseList(head.next)
head.next.next = head
head.next = None
return reversed_head
```