-
Notifications
You must be signed in to change notification settings - Fork 0
Create 2. Add Two Numbers.md #7
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,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 | ||||||
|
Comment on lines
+156
to
+157
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. 参考までにこちらはdivmodでまとめることも可能ですね |
||||||
| node.next = ListNode(value) | ||||||
| node = node.next | ||||||
|
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. 空行入れたくなる感覚をまだつかみ切れていないので助かります。 |
||||||
| 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) | ||||||
|
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した値は必要無いはずなのでreturn無しで終わらせるのが自然かと思いました
Suggested change
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.
|
||||||
|
|
||||||
| dummy = ListNode(None) | ||||||
| node = dummy | ||||||
| add_two_numbers(l1, l2, 0, node) | ||||||
| return dummy.next | ||||||
|
|
||||||
| ``` | ||||||
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.
個人的には逆に表記ゆれのように見えてしまうかもしれません。
また厳密には
if l1 is not None:とif l1:では違う判定をしているので、判定が異なることに何か意味があるのかと思われるかなと思いました。Uh oh!
There was an error while loading. Please reload this page.
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.
確かに
if l1 is not None:はl1とNoneの非同一性のチェックですが、if l1:はl1がTrueがどうかのチェックですね。意識していませんでした。https://docs.python.org/ja/3.13/library/stdtypes.html#truth-value-testing