-
Notifications
You must be signed in to change notification settings - Fork 0
206. Reverse Linked List.md #7
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
URL: https://leetcode.com/problems/reverse-linked-list/description/ | ||
|
||
# Step 1 | ||
|
||
線形リストを順に辿るやり方と、再帰を使うやり方を思いついた。 | ||
|
||
- 実装時間: 10分 | ||
- 時間計算量: O(n) | ||
- 空間計算量: O(1) | ||
|
||
```python | ||
class Solution: | ||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
node = head | ||
reversed_nodes = None | ||
while node is not None: | ||
next_node = node.next | ||
node.next = reversed_nodes | ||
reversed_nodes = node | ||
node = next_node | ||
return reversed_nodes | ||
``` | ||
|
||
- 実装時間: 5分 | ||
- 時間計算量: O(n**2) | ||
- 空間計算量: O(1) | ||
|
||
```python | ||
class Solution: | ||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
def append_linked_list(head, node): | ||
if head is None: | ||
return node | ||
current = head | ||
while current.next is not None: | ||
current = current.next | ||
current.next = node | ||
return head | ||
|
||
if head is None: | ||
return None | ||
reversed_tail = self.reverseList(head.next) | ||
head.next = None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これ、実は結果的に head.next がある場合には reversed_tail の一番後ろのノードになっているので、None かどうかをチェックして、最後にぶらさげると全部調べる必要がないんですよね。ただ、それだとちょっとテクニカルで読みにくいですね。その場合は self.reverseList を呼ぶ前に head.next をあらかじめ変数に入れておいたら分かりやすいと思います。 |
||
return append_linked_list(reversed_tail, head) | ||
``` | ||
|
||
反転したリストの末尾を毎回求めているため計算量が増えた。計算量を削減するためのヘルパー関数を用いる再帰パターン。 | ||
|
||
- 実装時間: 5分 | ||
- 時間計算量: O(n) | ||
- 空間計算量: O(1) | ||
|
||
```python | ||
class Solution: | ||
# returns head node and last node | ||
def _reverseListRecursive(self, head: Optional[ListNode]) -> Tuple[Optional[ListNode],Optional[ListNode]]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. , のあとにスペースを入れるとよいと思います。 |
||
if head is None: | ||
return None, None | ||
if head.next is None: | ||
return head, head | ||
reversed_head, reversed_last = self._reverseListRecursive(head.next) | ||
reversed_last.next = head | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. next に None を代入したあと、 next に一つ前のノードを代入しなおしているあたりが複雑に感じました。 step2 の実装のほうがシンプルに感じられました。 |
||
head.next = None | ||
return reversed_head, head | ||
|
||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
return self._reverseListRecursive(head)[0] | ||
``` | ||
|
||
# Step 2 | ||
|
||
- `reversed_nodes`の変数名について | ||
- 指しているのはreverseしたリストの先頭ノード | ||
- 複数の要素を持つ配列を指しているわけじゃなくてあくまで単一ノードを指している。 | ||
- `reversed_head`がよさそう | ||
|
||
- 再帰で実装したパターン | ||
- reverseListの末尾まで辿らなくても、元リストのnode.nextが末尾なことは問題からわかるので、計算しなくても良い。 | ||
- https://github.com/tarinaihitori/leetcode/pull/6/files | ||
|
||
- 補助関数の関数名について | ||
- Auxiliary や Helper などをつけて呼ばないことを想定しているとはっきりさせる | ||
- 直接ユーザーが呼ぶことの想定されない内部関数で、引数が増やしてあるなどほとんど元と同じものの場合は、名前を、元の関数の名前 + Helper にするのは一つの選択肢でしょう。 | ||
- https://github.com/hroc135/leetcode/pull/7/files#r1690169396 | ||
|
||
- 他の方のコードを見て見つけた別パターン | ||
- いったんStackにすべてのNodeを積んで、逆順に繋いでいくパターン。 | ||
- 再帰は2パターンある。 | ||
- 後ろからひっくり返していく方法がある気がします。後ろからひっくり返ったものの頭と尻尾をもらって、そこに自分のを外して付け直します。 | ||
- 僕はこっち。 | ||
- `reverseListRecursive(current, previous *ListNode)`を使って、先頭ノードを付け替えていくパターン。 | ||
|
||
別パターンの再帰も実装してみる。 | ||
|
||
```python | ||
class Solution: | ||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
def reverseListHelper(current_head: Optional[ListNode], reversed_head: Optional[ListNode]) -> Optional[ListNode]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 元のリンクリストにおいては、 reversed_head が一つ前、 current_head が現在注目しているノードとなっているため、引数も reversed_head, current_head の順にしたほうが直感的だと感じました。 また head はリンクリストの先頭のノードを表す言葉であり、関数の呼び出しごとに異なるノードを表している点に違和感を感じました。 |
||
if current_head is None: | ||
return reversed_head | ||
next_node = current_head.next | ||
current_head.next = reversed_head | ||
return reverseListHelper(next_node, current_head) | ||
return reverseListHelper(head, None) | ||
``` | ||
|
||
- 関数名はUpperCamel | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. あ、関数名をUpperCamelにするのはC++のケースで、言語やコーディング規約によって変わってくると思います。Pythonのコーティング規約のPEP8だと全て小文字のsnake_caseらしいです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます! 以下自分の調べたメモ:
|
||
- https://github.com/konnysh/arai60/pull/7/files#r1845471829 | ||
- 本来は先頭も大文字だけど、周囲(Leetcodeで指定されている関数名)に合わせるために、先頭小文字の補助関数の名前をつける。 | ||
|
||
# Step 3 | ||
|
||
この再帰の書き方が綺麗に思った。再帰のオーバーヘッドや呼び出し回数上限に気を配る必要があるがこの書き方を選ぶ。 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なぜ綺麗と思いましたか?綺麗と読みやすいはイコールですか? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ちょっと言語化するために考えてみました。
レビュー依頼時点で綺麗と思った理由は、「注目するものの数が少ないと感じた」からです。ヘルパー関数の2つの引数という形で(仕事の引き継ぎという比喩における)引き継がれるものが明確に渡されており、そこに注目して処理するだけで済む感覚を持ったからです。 ただ、今考えると、「注目するものの数が少ない」というのは勘違いかもしれないと気づきました
大前提、Step 3の選定において、他者が読みやすいという観点を持てていなかったので、反省します。
|
||
|
||
- 時間計算量: O(max(n,m)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mはどこから来ましたか? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コピペミスです>< 線形リストを一度走査するだけなので、
|
||
- 空間計算量: O(max(n,m)) | ||
|
||
```python | ||
class Solution: | ||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なんか全体的に命名が分かりづらいなと思いました。
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。 日本語で説明してみると、それぞれ
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. node_in_originalとnode_in_reversedあたりが端的で良いかなと思います。命名は難しいですね。 |
||
def reverseListHelper(current_head: Optional[ListNode], reversed_head: Optional[ListNode]) -> Optional[ListNode]: | ||
if current_head is None: | ||
return reversed_head | ||
next_node = current_head.next | ||
current_head.next = reversed_head | ||
return reverseListHelper(next_node, current_head) | ||
return reverseListHelper(head, None) | ||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
空間計算量はO(N)になってしますが、時間計算量O(N)で一度スタックに入れてから順に取り出していく方法もあるかなと思いました。(個人的には一番最初に思いついた解法でした。)
意図的にスキップしていたらすみません。