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
111 changes: 111 additions & 0 deletions problems/2.add-two-numbers/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
## step1
- エレガントな解法は思いつかなかった
- 2つとも数字に直して、足して一番大きい桁から新しく、連結リストを作る方法しか思いつかなかった
- 一回書いてみる
- 最後小さい桁からheadに入れないといけないので合計の数字を文字列にして逆順で取り出す
- headを特別に処理したくないので、前回勉強したdummy.nextを使ってみた

```
def addTwoNumbers(
self, l1: Optional[ListNode], l2: Optional[ListNode]
) -> Optional[ListNode]:
l1_toal = 0
l2_toal = 0

order = 0
while l1:
l1_toal = l1_toal + l1.val * (10**order)
l1 = l1.next
order += 1
order = 0

while l2:
l2_toal = l2_toal + l2.val * (10**order)
l2 = l2.next
order += 1

total = l1_toal + l2_toal

total_string = str(total)

dummy = ListNode()
node = dummy
for num in total_string[::-1]:
node.next = ListNode(int(num))
node = node.next

return dummy.next
```

- 解いた後再度確認すると最初に読んだ際に問題を誤読していて、小さい桁から値が入ってくるのであれば普通に足していけばいい気がしてきた
- 出た順に足していった

## step2
- 変数名や処理も改善する部分がなさそうだと思ったので、他の人のコードを見てみる
- このくらいなら関数化しなくてもいいのかなと思った
- 関数化するなら合計の数字の繰り上げ処理のところとか、加算部分を関数化すると思う

- https://github.com/tNita/arai60/pull/6/files
```
[nits]
self に依存しないことを明示するために @staticmethod にする、というのもありそうです。
```
- 勉強になった。selfに依存しない場合@staticmethodと明記する。普段の業務でTSで開発しているとstaticとよく書くがpythonで開発していないためあまり気にしたことがなかったので気にしたい
- 計算量書くのは良いので真似する

- https://github.com/Yoshiki-Iwasa/Arai60/pull/4
- 私のコードではhasCarryとしてbooleanにしていたが、他の局面では繰り上がりは2以上になることがあるので今後の拡張性を意識してint型のcarryを使った方がいいかも?
- https://github.com/yus-yus/leetcode/pull/5/files/91f344d110d2dc8ce7982065314a25afec963cc9#r1944795971
- `carry, digit = divmod(total, 10)`
- divmodという組み込み関数を知らなかった
- よくやる作業だし知らなくても関数名から意味がわかるので使っていきたい
- https://github.com/yus-yus/leetcode/pull/5
- 下記のように書く
```
if not l1: l1 = ListNode(0)
if not l2: l2 = ListNode(0)
```
- 私の今のコードの場合whileの後の4行が不要になりスッキリするので採用する
- 他の人が見た場合でも伝わるはず
- https://github.com/SanakoMeine/leetcode/pull/6#discussion_r1901296375
- この指摘はされないように気をつけたい
- 意図せず書いた場合はかなり印象悪くなりそう
- 数字や文字列は気にしなくてよくて、配列やobjectの場合は気にしているがある程度は慣れているので問題なさそうだが、LinkedListとかはかなり不安感ある
- https://github.com/mamo3gr/arai60/pull/5/files
- この方のコード見やすいのと他の方のコードについたコメントで気をつける点が全て反映されているような理想的なコードに見えた
- zipは見たことある,zip_longestは知らなかったが自分で書けるようになると今回のようなケースで便利
- yieldは説明見てもあんまりピンとこないから怖くて使ってない
- 特にreturnや末尾で処理が止まらないところがいかつすぎる
- これ見たらイメージ湧いたかもしれない(https://qiita.com/shun_sakamoto/items/5f13fa96701ba3727dc0)
- これはyieldやzip_longestを使ってて上級者の書き方だと思った
```
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
def generate_digits(l):
while l:
yield l.val
l = l.next
dummy = ListNode()
node = dummy
carry = 0
for num1, num2 in zip_longest(generate_digits(l1), generate_digits(l2), fillvalue=0):
total = num1 + num2 + carry
node.next = ListNode(total % 10)
node = node.next
carry = total // 10
if carry:
node.next = ListNode(carry)
return dummy.next
```

- 一旦下記を対応する
- 番兵
- divmod
- carryをintにする
- 元の引数を変えない

空間計算量: O(n)
時間計算量: O(n)

## step3
- 何回も消して書くと頭のリソースの使用量がだんだん小さくできている感覚がある

48 changes: 48 additions & 0 deletions problems/2.add-two-numbers/step1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#
# @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
class Solution:
def addTwoNumbers(
self, l1: Optional[ListNode], l2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
fixed_node = dummy
hasCarry = False
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

変数名に lower_snake と lowerCamel が混ざっているのが気になりました。 lower_snake で統一することをお勧めいたします。

参考までにスタイルガイドへのリンクを共有いたします。

https://peps.python.org/pep-0008/#function-and-variable-names

mixedCase is allowed only in contexts where that’s already the prevailing style (e.g. threading.py), to retain backwards compatibility.

なお、このスタイルガイドは“唯一の正解”というわけではなく、数あるガイドラインの一つに過ぎません。チームによって重視される書き方や慣習も異なります。そのため、ご自身の中に基準を持ちつつも、最終的にはチームの一般的な書き方に合わせることをお勧めします。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

識別子が動詞の命令形もしくは三単現で始まっていると、関数名のように感じます。 step2 で既に修正されている通り、 carry で十分に伝わると思います。


while l1 or l2 or hasCarry:
# 繰り上げのみ
if l1 is None and l2 is None:
total = 1
elif l1 is None:
total = l2.val + 1 if hasCarry else l2.val
elif l2 is None:
total = l1.val + 1 if hasCarry else l1.val
else:
total = l1.val + l2.val + 1 if hasCarry else l1.val + l2.val

if total > 9:
hasCarry = True
total -= 10
else:
hasCarry = False

fixed_node.next = ListNode(total)
fixed_node = fixed_node.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next

return dummy.next


# @lc code=end
40 changes: 40 additions & 0 deletions problems/2.add-two-numbers/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# @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
class Solution:
def addTwoNumbers(
self, l1: Optional[ListNode], l2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
fixed_node = dummy
Comment on lines +17 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

同じ値を複数の変数に定義する場合は一行にまとめることが可能です。
fixed_node = dummy = ListNode()

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.

ご指摘ありがとうございます。
確かにスッキリ書けるので今後そちらを使っていこうと思います。


n1, n2 = l1, l2
carry = 0

while n1 or n2 or carry:
v1 = n1.val if n1 else 0
v2 = n2.val if n2 else 0

total = v1 + v2 + carry
carry, fixed_node_val = divmod(total, 10)
fixed_node.next = ListNode(fixed_node_val)
fixed_node = fixed_node.next

if n1:
n1 = n1.next
if n2:
n2 = n2.next

return dummy.next


# @lc code=end
41 changes: 41 additions & 0 deletions problems/2.add-two-numbers/step3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# @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
class Solution:
def addTwoNumbers(
self, l1: Optional[ListNode], l2: Optional[ListNode]
) -> Optional[ListNode]:
dummy = ListNode()
fixed_node = dummy
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[nits]
ここではnodeが具体的に桁 (digit) を表しているので、そのような具体的な命名がより親切に思いました。

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.

ありがとうございます
確かに教えていただいた方が何をやっているかイメージが湧きやすく優れていると思いました。

carry = 0

n1, n2 = l1, l2
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私は「引数は変更しない」ことをどこかで教わってきたのですが、必ずしもそうではなさそうです。例えば
DaisukeKikukawa/LeetCode_arai60#5 (comment) の議論を参照ください。

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.

ありがとうございます
確認したところmamo3grさんと同じ理由で同じスタンスだったのですが、他の方にも変更されても気にしないスタンスの方がいることは認識しておこうと思いました。
現状の理解だと引数の中身を変更を変更することが目的の関数以外で引数の中身を変更した方が好ましいようなことがなければ、無理に今のスタンスを変えて合わせにいかなくてもいいのかなと考えております。


while n1 or n2 or carry:
val1 = n1.val if n1 else 0
val2 = n2.val if n2 else 0

total = val1 + val2 + carry
carry, fixed_node_val = divmod(total, 10)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私も10を使ってしまっているんですが、マジックナンバーになりそうですね。
定数化して名前をつけた方がよさそうです。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私は10が10進法の10であるというのは通じると思うのでこれでもいいかと思います。
後から変更したくなった時に10が散らかっていると大変ではあります。

fixed_node.next = ListNode(fixed_node_val)
fixed_node = fixed_node.next

if n1:
n1 = n1.next
if n2:
n2 = n2.next

return dummy.next


# @lc code=end