Skip to content

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

katataku
Copy link
Owner

return reverseListHelper(head, None)
```

- 関数名はUpperCamel
Copy link

Choose a reason for hiding this comment

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

あ、関数名をUpperCamelにするのはC++のケースで、言語やコーディング規約によって変わってくると思います。Pythonのコーティング規約のPEP8だと全て小文字のsnake_caseらしいです。
https://peps.python.org/pep-0008/#function-and-variable-names
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.

ありがとうございます!

以下自分の調べたメモ:

Copy link

@hayashi-ay hayashi-ay left a comment

Choose a reason for hiding this comment

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

個人的には再帰なのかループなのかという書き方の違いよりかは、この関数が破壊的なのか非破壊的なのかの方が興味があります。


```python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

Choose a reason for hiding this comment

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

なんか全体的に命名が分かりづらいなと思いました。

current_headのcurrentには現在注目しているくらいの意味しかなくて、reversed_headとどう違うんだろうみたいな感想を持ちました。あとは、reversed_headとありますが、current_headもreverseしてるじゃんみたいな気持ちになりました。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。

日本語で説明してみると、それぞれ

  • 「元のリストを順番に見ていくための先頭ノード」
  • 「これから逆順のリストを作っていく。その作業の途中の先頭ノード」
    が言いたいことなので、ichikaさんの例のようにoriginalを含めるべきだと気づきました。


この再帰の書き方が綺麗に思った。再帰のオーバーヘッドや呼び出し回数上限に気を配る必要があるがこの書き方を選ぶ。

- 時間計算量: O(max(n,m))

Choose a reason for hiding this comment

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

mはどこから来ましたか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

コピペミスです><

線形リストを一度走査するだけなので、

  • 時間計算量: O(n)
  • 空間計算量: O(1)


# Step 3

この再帰の書き方が綺麗に思った。再帰のオーバーヘッドや呼び出し回数上限に気を配る必要があるがこの書き方を選ぶ。

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.

ちょっと言語化するために考えてみました。

なぜ綺麗と思いましたか?

レビュー依頼時点で綺麗と思った理由は、「注目するものの数が少ないと感じた」からです。ヘルパー関数の2つの引数という形で(仕事の引き継ぎという比喩における)引き継がれるものが明確に渡されており、そこに注目して処理するだけで済む感覚を持ったからです。

ただ、今考えると、「注目するものの数が少ない」というのは勘違いかもしれないと気づきました
多分whileループで処理する時と、注目している要素の数は同じはず。。僕のwhileループ側の理解が甘いからそう感じたのかもと思いました

綺麗と読みやすいはイコールですか?

大前提、Step 3の選定において、他者が読みやすいという観点を持てていなかったので、反省します。
その上で、いまも「ヘルパー関数の方が読みやすい」はずだとも思っています。一方気になる点も残っていて

  • 他の人はループで書いてる人が多かった
    • そういえば、この問題に限らずループを選択している割合が多いような気がする。
    • 読み出し回数上限や関数呼び出しのオーバーヘッドがあるから避けてるのかと思ってましたが、そもそもループの方が直感的なのかもしれないと気づきました。


```python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

Choose a reason for hiding this comment

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

node_in_originalとnode_in_reversedあたりが端的で良いかなと思います。命名は難しいですね。

if head is None:
return None
reversed_tail = self.reverseList(head.next)
head.next = 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 がある場合には reversed_tail の一番後ろのノードになっているので、None かどうかをチェックして、最後にぶらさげると全部調べる必要がないんですよね。ただ、それだとちょっとテクニカルで読みにくいですね。その場合は self.reverseList を呼ぶ前に head.next をあらかじめ変数に入れておいたら分かりやすいと思います。

```python
class Solution:
# returns head node and last node
def _reverseListRecursive(self, head: Optional[ListNode]) -> Tuple[Optional[ListNode],Optional[ListNode]]:
Copy link

Choose a reason for hiding this comment

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

, のあとにスペースを入れるとよいと思います。
Tuple[Optional[ListNode], Optional[ListNode]]:

if head.next is None:
return head, head
reversed_head, reversed_last = self._reverseListRecursive(head.next)
reversed_last.next = head
Copy link

Choose a reason for hiding this comment

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

next に None を代入したあと、 next に一つ前のノードを代入しなおしているあたりが複雑に感じました。 step2 の実装のほうがシンプルに感じられました。

```python
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
def reverseListHelper(current_head: Optional[ListNode], reversed_head: Optional[ListNode]) -> Optional[ListNode]:
Copy link

Choose a reason for hiding this comment

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

元のリンクリストにおいては、 reversed_head が一つ前、 current_head が現在注目しているノードとなっているため、引数も reversed_head, current_head の順にしたほうが直感的だと感じました。

また head はリンクリストの先頭のノードを表す言葉であり、関数の呼び出しごとに異なるノードを表している点に違和感を感じました。


# Step 1

線形リストを順に辿るやり方と、再帰を使うやり方を思いついた。

Choose a reason for hiding this comment

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

空間計算量はO(N)になってしますが、時間計算量O(N)で一度スタックに入れてから順に取り出していく方法もあるかなと思いました。(個人的には一番最初に思いついた解法でした。)
意図的にスキップしていたらすみません。

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        stack = []
        cur = head
        while cur:
            stack.append(cur)
            cur = cur.next

        reversed_head = ListNode(None)
        reversed_cur = reversed_head

        while stack:
            node = stack.pop()
            reversed_cur.next = ListNode(node.val)
            reversed_cur = reversed_cur.next

        return reversed_head.next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants