Skip to content
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
215 changes: 215 additions & 0 deletions 2. Add Two Numbers/2. Add Two Numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
問題文: https://leetcode.com/problems/add-two-numbers/description/

# step1: 何も見ないで書く (制限時間5分)
- 時間計算量O(n)、空間計算量O(n)。
- 一番素直な方法だと思ったループと条件分岐で書いた。
- 素直だと感じた理由は、linked list だから各ノードごとに順番に和を取って各桁の値と繰り上がりの有無を計算していけると思ったから。
- 再帰だと、ノードの和を取ったあと次の桁の計算のために関数を再帰的に呼び出す、という発想になるのだと思うがループよりも非直観的な気がする。単に私が再帰になじんでいないだけかもしれないが。
- 再帰についてはstep2の課題とする。
- ループ条件は省略せずに`if l1 is not None`まで書き、ループ内の条件は`if l1`などと省略した。
- ループの部分で条件が明示されてるので条件分岐のところで省略しても可読性は下がらないのではないか、という予想。
Comment on lines +9 to +10
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

個人的には逆に表記ゆれのように見えてしまうかもしれません。
また厳密にはif l1 is not None:if l1:では違う判定をしているので、判定が異なることに何か意味があるのかと思われるかなと思いました。

Copy link
Copy Markdown
Owner Author

@TrsmYsk TrsmYsk Oct 12, 2025

Choose a reason for hiding this comment

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

確かにif l1 is not None: はl1とNoneの非同一性のチェックですが、if l1: はl1がTrueがどうかのチェックですね。意識していませんでした。
https://docs.python.org/ja/3.13/library/stdtypes.html#truth-value-testing

- リストや変数の名づけに少し迷った。
- 結果を格納するリストを`answer`や`result`などとすると、返すものが`answer.next`などとなり違和感がある。とりあえず`dummy`を採用。
- リストをつないでいくための変数もとりあえず`digit`としたが、計算の結果が`digit.next`に格納されるので微妙な気がする。
- 分岐の条件はもっとすっきり書けそうだけど、先に進むことを優先しこのままとする。
- リストが表す整数をint型に直してから和を取る方法もあると思ったが、リストを返すときはループが2回必要になり非効率なのでやらないほうがいい気がする。
- 新井さんの解説によると、リストを使った足し算は大きな桁数の数を計算するときの方法らしいので、そもそもint型への変換はできない・やらないほうがいい想定でいたほうがよさそう。\
https://youtu.be/VU_B3j-Mvps?si=rqRy7Xbr04Q8TLkU
- 面接や実際のお仕事では、なぜ数値がリストの形式で保持されているか質問して状況を明確にしておいた方がいい気がする。
```python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode()
digit = dummy
base_number = 10
carry = 0
while l1 is not None or l2 is not None or carry != 0:
if l1 and l2:
total = l1.val + l2.val + carry
result = total % base_number
carry = total // base_number
l1 = l1.next
l2 = l2.next
elif l1:
total = l1.val + carry
result = total % base_number
carry = total // base_number
l1 = l1.next
elif l2:
total = l2.val + carry
result = total % base_number
carry = total // base_number
l2 = l2.next
elif carry:
result = carry
carry = 0
digit.next = ListNode(result)
digit = digit.next
return dummy.next

```

# step2: 過去のコメントを参考にコードを整える
## 2-1 ループ
https://github.com/yas-2023/leetcode_arai60/pull/5/commits/8cd07f89599365c775c13018f8aabe09a1601cdd#r2390891628
- `total`を`carry`で初期化しておいて、ノードが`None`でなければ`.val`を足す方法。処理の流れも追いやすいし、コードも短くなって読みやすいので、この方針で整える。
- step1では`dummy`の値を0のままにしてしまっていた。念のため`None`で初期化しておく。
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(None)
node = dummy
base_number = 10
carry = 0
while l1 is not None or l2 is not None or carry == 1:
total = carry
if l1:
total += l1.val
l1 = l1.next
if l2:
total += l2.val
l2 = l2.next
carry = total // base_number
value = total % base_number
node.next = ListNode(value)
node = node.next
return dummy.next

```

## 2-2 再帰
https://discord.com/channels/1084280443945353267/1196472827457589338/1197166381146329208
- 再帰に帰りがけと行きがけの2種類があることを知る。両社の性質の違いなどはよくわからない。

### 帰りがけの再帰
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def add_two_numbers(list1: Optional[ListNode], list2: Optional[ListNode], carry: Optional[int]):
if list1 is None and list2 is None and carry == 0:
return None
total = carry
if list1:
total += list1.val
list1 = list1.next
if list2:
total += list2.val
list2 = list2.next
value = total % 10
next_carry = total // 10
node = ListNode(val=value, next=add_two_numbers(list1, list2, next_carry))
return node

return add_two_numbers(l1,l2,0)

```
### 行きがけの再帰
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def add_two_numbers(list1: Optional[ListNode], list2: Optional[ListNode], carry: Optional[int], node: Optional[ListNode]):
if list1 is None and list2 is None and carry == 0:
return None
total = carry
if list1:
total += list1.val
list1 = list1.next
if list2:
total += list2.val
list2 = list2.next
value = total % 10
next_carry = total //10
node.next = ListNode(value)
node = node.next
return add_two_numbers(list1, list2, next_carry, node)

dummy = ListNode(None)
node = dummy
add_two_numbers(l1, l2, 0, node)
return dummy.next

```


# step3: 3回連続ミスなしで書く (制限時間10分)
## 3-1 ループ
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(None)
node = dummy
base_number = 10
carry = 0
while l1 is not None or l2 is not None or carry != 0:
total = carry
if l1:
total += l1.val
l1 = l1.next
if l2:
total += l2.val
l2 = l2.next
carry = total // base_number
value = total % base_number
Comment on lines +156 to +157
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

参考までにこちらはdivmodでまとめることも可能ですね
https://docs.python.org/ja/3/library/functions.html#divmod

node.next = ListNode(value)
node = node.next
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

全体的に詰まっている感じがあるのでこの後に一行空行を入れてもいいかもしれません。
好みだと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

空行入れたくなる感覚をまだつかみ切れていないので助かります。

return dummy.next

```

## 3-2 再帰
### 帰りがけの再帰
- `return`のインデントが再帰関数の内側まで下がっているのを見落として何回かミスを出した。対策として先に`return`を定義するようにしたが、他に何かよい方法はあるだろうか。
- 再帰関数内部の処理も関数化すればコードがもう少し短くなってインデントまで気を配れるだろうか。
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def add_two_numbers(list1: Optional[ListNode], list2: Optional[ListNode], carry: Optional[int]):
if list1 is None and list2 is None and carry == 0:
return None
total = carry
if list1:
total += list1.val
list1 = list1.next
if list2:
total += list2.val
list2 = list2.next
next_carry = total // 10
value = total % 10
node = ListNode(value, add_two_numbers(list1, list2, next_carry))
return node

return add_two_numbers(l1, l2, 0)

```

### 行きがけの再帰
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def add_two_numbers(list1: Optional[ListNode], list2: Optional[ListNode], carry: Optional[int], node: Optional[ListNode]):
if list1 is None and list2 is None and carry == 0:
return None
total = carry
if list1:
total += list1.val
list1 = list1.next
if list2:
total += list2.val
list2 = list2.next
value = total % 10
node.next = ListNode(value)
node = node.next
next_carry = total // 10
return add_two_numbers(list1, list2, next_carry, node)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらの場合はreturnした値は必要無いはずなのでreturn無しで終わらせるのが自然かと思いました

Suggested change
return add_two_numbers(list1, list2, next_carry, node)
add_two_numbers(list1, list2, next_carry, node)

Copy link
Copy Markdown
Owner Author

@TrsmYsk TrsmYsk Oct 12, 2025

Choose a reason for hiding this comment

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

ご指摘のとおり、行きがけの再帰だと文字通り行きがけで各ステップの処理が完結するので、returnは再帰の底でNoneを返せば十分でした。
副作用でリストを作ってるので関数の戻り値はたしかに必要ないですね。


dummy = ListNode(None)
node = dummy
add_two_numbers(l1, l2, 0, node)
return dummy.next

```