-
Notifications
You must be signed in to change notification settings - Fork 0
Solve 2. Add Two Numbers #2
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,39 @@ | ||
| # step1 | ||
|
|
||
| time: 30:28 | ||
|
|
||
| 解法は2つ思いつく. | ||
|
|
||
| 一つは linked list と数字の相互変換メソッドを定義してそれらを使用する方法. time complexity は O(N+M). space complexity は O(1). | ||
|
|
||
| もう一つは同時に2つの linked list をたどりつつ新しい linked list を作る方法. time complexity は O(max(N, M)), space complexity は O(1). | ||
|
|
||
| わかりやすさをとるか, 定数倍の性能差をとるか. | ||
|
|
||
| 多分求められているのは後者の方だと思う&前回は前者で実装したので後者で実装してみる. carry に注意. full adder みたいなものになりそう. | ||
|
|
||
| l1, l2 が None じゃないかで 2 x 2 = 4 通りある時の条件分岐の書き方がいつもわからない. 素直に 4 つ書くのが無難か. | ||
|
|
||
| 変数の取り扱いが難しい. node を付け足す処理を共通化するなら digit_sum = None として事前に定義し後々値を入れればいいが, 考慮忘れの条件分岐に進んだとしたら None のままになっているのが怖い. | ||
|
|
||
| あと l1 = l1.next しても良いのか. 呼び出し元の l1 が変化してしまうのではないのか. Python の参照渡しとかリテラルに対する勉強が足りない. | ||
|
|
||
| l1, l2, carry ともに None のときは, sum_cursor の prev の next 変数を None にしないといけないが, そのためには sum_cursor_prev のような形で以前の位置の cursor を持っておく必要がある. うまく表現できないが, その一つのケースのために prev を持つのは複雑度が増して気持ち悪い感じがする. 代替案として, sum_root_node の一番最初の node は dummy の node を置いて, l1.val + l2.val + carry > 0 の場合はケツに node を追加するような実装にすれば問題なさそう. | ||
|
|
||
| なんとかできたが, すごい読みにくいコードな気がする. やはり前者の実装方式のほうが良かったな. | ||
|
|
||
| # step2 | ||
|
|
||
| leetcode の解答を見た感じ, dummy head を用いるというアイデアは良かった. | ||
| None を 0 とすることでコードを簡潔にできそうだ. | ||
| あと部分問題に分けることで, recursive に解くこともできる. | ||
|
|
||
| また, l1 = l1.next としても良いのか問題だが, l1 に再代入しているため問題ない. | ||
|
|
||
| https://github.com/Mike0121/LeetCode/tree/main/Arai60/02.%20Add%20Two%20Numbers | ||
| https://github.com/hayashi-ay/leetcode/pull/24/files | ||
| こちらを参考に, recursive と loop 版で実装してみる. | ||
|
|
||
| recursive で実装するときに, carry を考慮した関数を作るが, これはどこに置けばいいだろうか. メソッド内に関数を書くのはあまり経験がないので違和感を感じるが, carry を考慮した関数は外から呼び出すことはないので, メソッド内に書くのが良いのかもしれない. | ||
|
|
||
| 条件分岐もなくなり, digit_sum = None と初期化する問題も解消された. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Definition for singly-linked list. | ||
| # class ListNode: | ||
| # def __init__(self, val=0, next=None): | ||
| # self.val = val | ||
| # self.next = next | ||
| class Solution: | ||
| def listNodeToNumber(self, l: ListNode) -> int: | ||
| node = l | ||
| power = 1 | ||
| number = 0 | ||
|
|
||
| while node is not None: | ||
| number += power * node.val | ||
| power *= 10 | ||
| node = node.next | ||
|
|
||
| return number | ||
|
|
||
| def numberToListNode(self, number: int) -> ListNode: | ||
| start = ListNode(val=number % 10) | ||
| now = start | ||
| number //= 10 | ||
| while number > 0: | ||
| now.next = ListNode(val=number % 10) | ||
| now = now.next | ||
| number //= 10 | ||
| return start | ||
|
|
||
| def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: | ||
| l1num = self.listNodeToNumber(l1) | ||
| l2num = self.listNodeToNumber(l2) | ||
| return self.numberToListNode(l1num + l2num) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # 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]: | ||
| sum_root_node = ListNode() | ||
| sum_cursor = sum_root_node | ||
| carry = 0 | ||
| while True: | ||
| digit_sum = None | ||
| if l1 is None and l2 is None: | ||
|
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 文をシンプルにするため、
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. なるほど, たしかにおっしゃるとおりですね… |
||
| if carry != 0: | ||
| sum_cursor.next = ListNode(val=carry) | ||
| break | ||
| if l1 is not None and l2 is None: | ||
| digit_sum = carry + l1.val | ||
| l1 = l1.next | ||
| elif l1 is None and l2 is not None: | ||
| digit_sum = carry + l2.val | ||
| l2 = l2.next | ||
| else: | ||
| digit_sum = carry + l1.val + l2.val | ||
| l1 = l1.next | ||
| l2 = l2.next | ||
| carry = digit_sum // 10 | ||
| digit_value = digit_sum % 10 | ||
| sum_cursor.next = ListNode(val=digit_value) | ||
| sum_cursor = sum_cursor.next | ||
| return sum_root_node.next | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # 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]: | ||
| sum_dummy_head = ListNode() | ||
| sum_cursor = sum_dummy_head | ||
| carry = 0 | ||
| while l1 is not None or l2 is not None or carry > 0: | ||
| l1_digit = l1.val if l1 else 0 | ||
| l2_digit = l2.val if l2 else 0 | ||
| digit_sum = l1_digit + l2_digit + carry | ||
| carry = digit_sum // 10 | ||
| digit_val = digit_sum % 10 | ||
|
|
||
| sum_cursor.next = ListNode(digit_val) | ||
| sum_cursor = sum_cursor.next | ||
| l1 = l1.next if l1 else None | ||
| l2 = l2.next if l2 else None | ||
| return sum_dummy_head.next | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # 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]: | ||
| def add_two_numbers_helper(l1: Optional[ListNode], l2: Optional[ListNode], carry: int) -> Optional[ListNode]: | ||
| if l1 is None and l2 is None and carry == 0: | ||
| return None | ||
| l1_val = l1.val if l1 else 0 | ||
| l2_val = l2.val if l2 else 0 | ||
| digit_sum = l1_val + l2_val + carry | ||
| sum_head = ListNode(digit_sum % 10) | ||
| l1_next = l1.next if l1 else None | ||
| l2_next = l2.next if l2 else None | ||
| sum_head.next = add_two_numbers_helper(l1_next, l2_next, digit_sum // 10) | ||
| return sum_head | ||
| return add_two_numbers_helper(l1, l2, 0) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # 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]: | ||
| def add_two_numbers_helper(l1: Optional[ListNode], l2: Optional[ListNode], carry: int) -> Optional[ListNode]: | ||
| if l1 is None and l2 is None and carry == 0: | ||
| return None | ||
| l1_val = l1.val if l1 else 0 | ||
| l2_val = l2.val if l2 else 0 | ||
| digit_sum = l1_val + l2_val + carry | ||
|
|
||
| sum_root_node = ListNode(val=digit_sum%10) | ||
| l1_next = l1.next if l1 else None | ||
| l2_next = l2.next if l2 else None | ||
| sum_root_node.next = add_two_numbers_helper(l1_next, l2_next, digit_sum // 10) | ||
| return sum_root_node | ||
| return add_two_numbers_helper(l1, l2, 0) |
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.
Python で int に代入することのできる値の範囲はいくつからいくつまでですか?また、ほかの主要な言語ではどうですか?
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.
コメントありがとうございます!
Pythonではintにメモリが許す分大きな値をいれることができます. そのため, intに代入することのできる値の範囲はメモリの大きさや処理系(CPythonかどうか, 64bit platformか)に依存します. 今回はlinked listの長さがたかだか100だったから良かったものの, 長さが大きくなるとそういったところも考えなければいけなくなるのですね.
また, 他の主要な言語では可変長整数は一般的ではなく, 扱える値の範囲は決まっております.
このold.pyの解答はpythonだからこそ簡単そうに実装できたということで, もし高速化のためc++で実装しようとなったときにはこの方針は使えませんね.
大変勉強になりました.