From 155258b7c8b21f9ba5af07d931fd190adb54103e Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sat, 21 Jun 2025 15:38:26 +0900 Subject: [PATCH] Solve 2_add_two_numbers_medium --- 2_add_two_numbers_medium/README.md | 43 +++++++++++++++++ 2_add_two_numbers_medium/step1.py | 51 ++++++++++++++++++++ 2_add_two_numbers_medium/step2.py | 53 +++++++++++++++++++++ 2_add_two_numbers_medium/step2_recursive.py | 50 +++++++++++++++++++ 2_add_two_numbers_medium/step3.py | 49 +++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 2_add_two_numbers_medium/README.md create mode 100644 2_add_two_numbers_medium/step1.py create mode 100644 2_add_two_numbers_medium/step2.py create mode 100644 2_add_two_numbers_medium/step2_recursive.py create mode 100644 2_add_two_numbers_medium/step3.py diff --git a/2_add_two_numbers_medium/README.md b/2_add_two_numbers_medium/README.md new file mode 100644 index 0000000..dc8cee9 --- /dev/null +++ b/2_add_two_numbers_medium/README.md @@ -0,0 +1,43 @@ +# 問題へのリンク +[Add Two Numbers - LeetCode](https://leetcode.com/problems/add-two-numbers/description/) + +# 言語 +Python + +# 問題の概要 +与えられた2つの非負整数を逆順で表現した連結リストとして受け取り、それらの合計を同様に連結リストで返す問題。 + +# 自分の解法 +再帰関数を用いた解法。 + + +連結リストl1, l2の長さをそれぞれ`m`, `n`とすると +- 時間計算量:`O(max(m,n))` +- 空間計算量:`O(max(m,n))`(再帰のスタック領域を含む) + +## step2 +再帰的な実装から反復的な実装に変えた。また、変数名もより明確なものに変更した。 + +- アルゴリズムの変更:再帰から反復へ + - step1は再帰関数を用いた実装であったが、step2はwhileループを用いた反復処理の実装である。これにより、非常に長い連結リストを扱う際のスタックオーバーフローのリスクが解消された。 +- ダミーヘッドノードの導入 + - step2では、連結リストを構築する際の一般的なテクニックである`dummy_head`ノードが導入された。これにより、リストの先頭ノードに関する特別な処理が不要になり、コードが単純化されている。 +- `None`ノードの処理方法の改善 + - step1では、入力リストのノードが`None`の場合に新しい`ListNode`を生成していた。step2では、`l1.val if l1 else 0`という三項演算子を用いることで、この処理をより効率的かつ簡潔に記述している。 +- ループ条件の単純化 + - step1の複雑な再帰終了条件は、step2では`while l1 or l2 or carry_of_digits:`という単一の明快なループ条件に集約された。これは、処理を継続すべき全てのケースを網羅している。 + + +# 別解・模範解答 +再帰関数ではなく、ループを用いてノードを1つずつ処理する解法。 +最後に返すノードは連結リストの先頭ノードなので、ダミーノードを用意しておき、そこから結果の連結リストを構築する。その後、ダミーノードの次のノード(`dummy_head.next`)を返す。 +ループの最中には、今自分がどのノードを処理しているかをきちんと把握しておく。コードの中でもそれが明示できるように心がける。今回ならば、while文の中で足し合わせたノードは`node.next`に保存する。 + +連結リストl1, l2の長さをそれぞれ`m`, `n`とすると + +- 時間計算量:`O(max(m,n))` +- 空間計算量:`O(1)` + +# 次に解く問題の予告 + +- [ ] [Evaluate Division - LeetCode](https://leetcode.com/problems/evaluate-division/description/) diff --git a/2_add_two_numbers_medium/step1.py b/2_add_two_numbers_medium/step1.py new file mode 100644 index 0000000..701eca5 --- /dev/null +++ b/2_add_two_numbers_medium/step1.py @@ -0,0 +1,51 @@ +# +# @lc app=leetcode id=2 lang=python3 +# +# [2] Add Two Numbers +# + + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + + +from typing import Optional + +# we consider addition in base 10 +BASE_NUMBER = 10 + + +class Solution: + def addTwoNumbers( + self, l1: Optional[ListNode], l2: Optional[ListNode] + ) -> Optional[ListNode]: + + def add_two_list_nodes( + l1: Optional[ListNode], l2: Optional[ListNode], carry_of_digits: int + ) -> Optional[ListNode]: + if not l1 and not l2 and carry_of_digits == 0: + return None + elif not l1 and not l2: + l1 = ListNode() + l2 = ListNode() + elif not l1: + l1 = ListNode() + elif not l2: + l2 = ListNode() + + added_list_node: ListNode = ListNode() + carry_of_digits, digit_sum = divmod( + l1.val + l2.val + carry_of_digits, BASE_NUMBER + ) + added_list_node.val = digit_sum + added_list_node.next = add_two_list_nodes(l1.next, l2.next, carry_of_digits) + return added_list_node + + return add_two_list_nodes(l1, l2, 0) + + +# @lc code=end diff --git a/2_add_two_numbers_medium/step2.py b/2_add_two_numbers_medium/step2.py new file mode 100644 index 0000000..6ef9099 --- /dev/null +++ b/2_add_two_numbers_medium/step2.py @@ -0,0 +1,53 @@ +# +# @lc app=leetcode id=2 lang=python3 +# +# [2] Add Two Numbers +# + + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + + +from typing import Optional + +# we consider addition in base 10 +BASE_NUMBER = 10 + + +class Solution: + def addTwoNumbers( + self, l1: Optional[ListNode], l2: Optional[ListNode] + ) -> Optional[ListNode]: + """ + addTwoNumbers iteratively creates nodes and set added value to each of them. + Created linked list look like this; dummy_head -> node0 -> node1 ->... -> last_node + return value is dummy_head.next (=node0) + """ + # save dummy head for returning head node + dummy_head: ListNode = ListNode() + + # initialize first node and carry of digits + node = dummy_head + carry_of_digits = 0 + while l1 or l2 or carry_of_digits: + # add two values and carry_of_digits + digit1 = l1.val if l1 else 0 + digit2 = l2.val if l2 else 0 + total = digit1 + digit2 + carry_of_digits + carry_of_digits, digit = divmod(total, BASE_NUMBER) + node.next = ListNode(digit) + + # move to next node + node = node.next + l1 = l1.next if l1 else None + l2 = l2.next if l2 else None + + return dummy_head.next + + +# @lc code=end diff --git a/2_add_two_numbers_medium/step2_recursive.py b/2_add_two_numbers_medium/step2_recursive.py new file mode 100644 index 0000000..0fcfa00 --- /dev/null +++ b/2_add_two_numbers_medium/step2_recursive.py @@ -0,0 +1,50 @@ +# +# @lc app=leetcode id=2 lang=python3 +# +# [2] Add Two Numbers +# + + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + + +from typing import Optional + +# we consider addition in base 10 +BASE_NUMBER = 10 + + +class Solution: + def addTwoNumbers( + self, l1: Optional[ListNode], l2: Optional[ListNode] + ) -> Optional[ListNode]: + + def add_nodes_recursive( + l1: Optional[ListNode], l2: Optional[ListNode], carry_of_digits: int + ) -> Optional[ListNode]: + if not l1 and not l2 and carry_of_digits == 0: + return None + if not l1: + l1 = ListNode() + if not l2: + l2 = ListNode() + + added_list_node: ListNode = ListNode() + carry_of_digits, digit_sum = divmod( + l1.val + l2.val + carry_of_digits, BASE_NUMBER + ) + added_list_node.val = digit_sum + added_list_node.next = add_nodes_recursive( + l1.next, l2.next, carry_of_digits + ) + return added_list_node + + return add_nodes_recursive(l1, l2, 0) + + +# @lc code=end diff --git a/2_add_two_numbers_medium/step3.py b/2_add_two_numbers_medium/step3.py new file mode 100644 index 0000000..2fbacf9 --- /dev/null +++ b/2_add_two_numbers_medium/step3.py @@ -0,0 +1,49 @@ +# +# @lc app=leetcode id=2 lang=python3 +# +# [2] Add Two Numbers +# + + +# @lc code=start +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + + +from typing import Optional + +BASE_NUMBER = 10 + + +class Solution: + def addTwoNumbers( + self, l1: Optional[ListNode], l2: Optional[ListNode] + ) -> Optional[ListNode]: + # to save head node, we preapare head_dummy + head_dummy = ListNode() + + node: ListNode = head_dummy + carry_of_digits = 0 + + # visit each node iteratively and create added digit one by one + # break the loop if two nodes l1 and l2 are None and no carry of digits + while l1 or l2 or carry_of_digits: + # add two nodes + digit1 = l1.val if l1 else 0 + digit2 = l2.val if l2 else 0 + total = digit1 + digit2 + carry_of_digits + carry_of_digits, digit = divmod(total, BASE_NUMBER) + node.next = ListNode(digit) + + # move to next node + node = node.next + l1 = l1.next if l1 else None + l2 = l2.next if l2 else None + + return head_dummy.next + + +# @lc code=end