Skip to content

2. Add Two Numbers.md #4

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 3 commits 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
159 changes: 159 additions & 0 deletions 2. Add Two Numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

URL: https://leetcode.com/problems/add-two-numbers/description/

# Step 1

- 実装時間: 10分
- 時間計算量: O(max(n,m))
- 空間計算量: O(1)
- `Optional`の使い方だけ確認して解き始めた。

```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode(0,None)
Copy link

Choose a reason for hiding this comment

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

, のあとにスペースを空けることをおすすめいたします。

また、第一引数のデフォルト値が 0、第二引数のデフォルト値が None のため、引数は渡さなくてよいと思います。

node = sentinel
carry = 0
while l1 is not None or l2 is not None or carry > 0:
node.next = ListNode(0, None)
node = node.next
if l1 is not None:
node.val += l1.val
l1 = l1.next
if l2 is not None:
node.val += l2.val
l2 = l2.next
node.val += carry
if node.val >= 10:
node.val %= 10
carry = 1
else:
carry = 0
return sentinel.next

```

# Step 2

- node.valの初期値をcarryにする。 `carry = sum / 10`で計算できる。

- 空間計算量が間違えている。
- 答えの桁数に応じてNodeを増やしていくので、足し算の結果(和)`s`に対して`O(log s)`になる。
- 桁数に注目するなら、入力の桁数を`n, m`とすると`O(max(n, m))`。

- 別の書き方。
- 足すのとノードを動かすのを一緒にやるか、分けるかは好みですかね
- ヘルパー関数。carryは0, 1で持っている。`sum = getValue(l1) + getValue(l2) + carry`

```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode(0,None)
node = sentinel
carry = 0
while l1 is not None or l2 is not None or carry > 0:
node.next = ListNode(carry, None)
node = node.next
if l1 is not None:
node.val += l1.val
l1 = l1.next
if l2 is not None:
node.val += l2.val
l2 = l2.next
carry = node.val // 10
node.val %= 10
return sentinel.next
```

# Step 3

- 入力の桁数を`n, m`として、
- 時間計算量: O(max(n,m))
- 空間計算量: O(max(n,m))

```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:

Choose a reason for hiding this comment

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

なんかうまく言語化できないんですけど、結構読みづらいなと思いました。

L90のnode.valに何が入るかを知るのに、以下の5ステップをちゃんと追わないといけなくてこの短い行数でも結構しんどかったです。

  • L81でnode.nextにListNode(carry, None)でcarryが初期値として渡され
  • L82でそれがnodeに代入されて
  • L84でl1.valで足されて
  • L87でl2.valが足されて
  • 最後にL90で10で割った余りが入ってくる

Choose a reason for hiding this comment

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

あとはループの中でいきなり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.

ありがとうございます!率直なフィードバックありがたいです。

コメントを見て、やっぱり、読み手としはsum = getValue(l1) + getValue(l2) + carryのような、

  • 「足し算の結果の桁数を入れるよ」の意味が伝わるような変数を用意する。
  • 素直に足し算処理はまとめて書く。Node作ったり進めたりするのは足し算の処理とごっちゃにしない。

の方がわかりやすいのかなと気づきました。Step4として書き直してみます。

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.

https://discord.com/channels/1084280443945353267/1200089668901937312/1206180274442993694
あたりから主役が変わる話についてしています。

sentinel = ListNode()
node = sentinel
carry = 0
while l1 is not None or l2 is not None or carry > 0:
node.next = ListNode(carry, None)
node = node.next
if l1 is not None:
node.val += l1.val
l1 = l1.next
if l2 is not None:
node.val += l2.val
l2 = l2.next
carry = node.val // 10
node.val %= 10
return sentinel.next
```

# Step 4

- コメントをいただいたので、以下を反映。
- 「足し算の結果の桁数を入れるよ」の意味が伝わるような変数を用意する。
- 素直に足し算処理はまとめて書く。Node作ったり進めたりするのは足し算の処理とごっちゃにしない

```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def _get_value(node):
Copy link

Choose a reason for hiding this comment

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

inner function の関数名の先頭には _ を付けなくてよいと思います。

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

Choose a reason for hiding this comment

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

https://docs.python.org/3/tutorial/classes.html#private-variables

“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

Copy link

Choose a reason for hiding this comment

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

https://peps.python.org/pep-0008/#descriptive-naming-styles

_single_leading_underscore: weak “internal use” indicator. E.g. from M import * does not import objects whose names start with an underscore.

if node is None:
return 0
return node.val

sentinel = ListNode(0, None)
carry = 0
node = sentinel
while l1 is not None or l2 is not None or carry > 0:
sum = _get_value(l1) + _get_value(l2) + carry

Choose a reason for hiding this comment

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

sumはbuiltin関数なので使用はできるかぎり避けておくほうが無難だと思います。
https://docs.python.org/3/library/functions.html#sum

Copy link

Choose a reason for hiding this comment

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

carry = sum // 10
sum %= 10

node.next = ListNode(sum, None)
node = node.next

if l1 is not None:
l1 = l1.next
if l2 is not None:
l2 = l2.next
return sentinel.next
```

# Step 5

- コメントをいただいたので、以下を反映。
- inner functionの関数名の`_`はなくす
- builtin functionと変数名の重複は避ける。
- デフォルト値で初期化させる。

```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def get_value(node):
if node is None:
return 0
return node.val

sentinel = ListNode()
node = sentinel
carry = 0
while l1 is not None or l2 is not None or carry > 0:
sum_value = get_value(l1) + get_value(l2) + carry
carry = sum_value // 10
sum_value %= 10
Copy link

@hayashi-ay hayashi-ay Nov 16, 2024

Choose a reason for hiding this comment

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

自分はこれも別の変数にする派です。digit = sum_value % 10的な感じです

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.next = ListNode(sum_value)
node = node.next

if l1 is not None:
l1 = l1.next
if l2 is not None:
l2 = l2.next
return sentinel.next
```

- 指摘反映まで数日、期間が空いたがスムーズに書けたので、腹落ちしている書き方なのだと思う。