-
Notifications
You must be signed in to change notification settings - Fork 0
Solve 2_add_two_numbers_medium #10
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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/) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
参考までにスタイルガイドへのリンクを貼ります。 https://google.github.io/styleguide/pyguide.html#2144-decision
ただし、上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. レビューありがとうございます。 |
||
| return None | ||
| elif not l1 and not l2: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. step 2 のように if not l1:
l1 = ListNode()
if not l2:
l2 = ListNode()と書いたほうがシンプルだと思いました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。step2で改善できたようで良かったです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 直前が |
||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 個人的には carry = total // BASE_NUMBER
digit = total % BASE_NUMBERの方が unpack より読み下しやすいと感じてしまうのですが、単に私が
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. レビューありがとうございます。 一方で、それ以上に可読性が下がってしまうのがまずいとすると、後者を優先するべきだと思います。 【補足】バイトコードレベルでみると、割り算の結果とその余りは同時に得られるので、 実際、以下のような結果が得られます。 参考There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 私も |
||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
Comment on lines
+31
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (自分用のメモ、というだけであれば恐縮ですが) このコメントはコードを読めば明らかにわかることで、特段複雑な処理をしているわけでもありませんし、冗長なように感じます。コメントは何をしているかよりも、なぜそうしているのか、を言及することが多いかと思います。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かにそのコメントは不要に感じました。ありがとうございます。 |
||
| 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 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
単に carry でも十分に通じると思いました。