From 3298b4ddb3f37d63a067f158e0a6830424e6fd29 Mon Sep 17 00:00:00 2001 From: TrsmYsk <53941356+TrsmYsk@users.noreply.github.com> Date: Sat, 11 Oct 2025 17:53:55 +0900 Subject: [PATCH] Create 2. Add Two Numbers.md --- 2. Add Two Numbers/2. Add Two Numbers.md | 215 +++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 2. Add Two Numbers/2. Add Two Numbers.md diff --git a/2. Add Two Numbers/2. Add Two Numbers.md b/2. Add Two Numbers/2. Add Two Numbers.md new file mode 100644 index 0000000..0ef02a1 --- /dev/null +++ b/2. Add Two Numbers/2. Add Two Numbers.md @@ -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`などと省略した。 + - ループの部分で条件が明示されてるので条件分岐のところで省略しても可読性は下がらないのではないか、という予想。 +- リストや変数の名づけに少し迷った。 + - 結果を格納するリストを`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 + node.next = ListNode(value) + node = node.next + 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) + + dummy = ListNode(None) + node = dummy + add_two_numbers(l1, l2, 0, node) + return dummy.next + +```