Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions 2. Add Two Numbers/note.md
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 と初期化する問題も解消された.
34 changes: 34 additions & 0 deletions 2. Add Two Numbers/old.py
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python で int に代入することのできる値の範囲はいくつからいくつまでですか?また、ほかの主要な言語ではどうですか?

Copy link
Copy Markdown
Owner Author

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++で実装しようとなったときにはこの方針は使えませんね.
大変勉強になりました.


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)


32 changes: 32 additions & 0 deletions 2. Add Two Numbers/step1.py
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:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while 文をシンプルにするため、 l1 is None and l2 is None を while 文の条件式にし、 carry の処理を while 文の外で行ったほうがよいと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど, たしかにおっしゃるとおりですね…
とりあえずwhileをwhile Trueで始めてしまうクセがあるので気をつけます.
ありがとうございます.

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

24 changes: 24 additions & 0 deletions 2. Add Two Numbers/step2_loop.py
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


20 changes: 20 additions & 0 deletions 2. Add Two Numbers/step2_recursive.py
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)

20 changes: 20 additions & 0 deletions 2. Add Two Numbers/step3.py
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)