Skip to content
Open
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
121 changes: 121 additions & 0 deletions 2. Add Two Numbers/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
## step1:
まずl1, l2のheadからポインタを順番に線形探索していって、各ノードでvalを足してもし繰り上げがあればそれを次のノード同士での足し算で利用すれば実装できそうだと感じた。なので答えをanswer_headとしてrunnerに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.

何らかの値を探索しているわけではないのに、線形探索と呼ぶのは誤用じゃないでしょうか?

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.

O(N)のものはなんでも線形探索って言ってました...
正しくは線形走査(linear scan)ですね

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かいですが、「繰り上げ」ではなく、「繰り上がり」ではないですか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なので答えをanswer_headとしてrunnerにl1とl2の各ノードの足した結果を新しいノードとして順次生成してもらってそれを答えとする方針でいく。

コードを見ると、まずノードを作ってから、次のループで値を計算して代入しているので、説明と一致していなさそうですかね?

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.

たしかにこの説明はほかの人のdummyを使った解法で当てはまる説明ですね。
自分の解法はrunnerに値を代入する予定のノードを作成してそこにl1, l2, 繰り上がりの値を足した結果を代入する方針ですね。

### code
```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]:
carry_up = 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.

変数名は、carry_upであっていますか?

https://en.wikipedia.org/wiki/Carry_(arithmetic)

answer_head = ListNode()
runner = answer_head
while l1 or l2 or carry_up > 0:
val_sum = 0
if l1:
val_sum += l1.val
l1 = l1.next
if l2:
val_sum += l2.val
l2 = l2.next
val_sum += carry_up

runner.val = val_sum % 10
carry_up = val_sum // 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.

if l1 or l2 or carry_up > 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.

l1 or l2 or carry_up > 0の重複を無くせそうでしょうか?

Copy link
Copy Markdown
Owner Author

@atmaxstar atmaxstar May 9, 2026

Choose a reason for hiding this comment

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

while True:
    val_sum = 0
    if l1:
        val_sum += l1.val
        l1 = l1.next
    if l2:
        val_sum += l2.val
        l2 = l2.next
    val_sum += carry
    runner.val = val_sum % 10
    carry = val_sum // 10

    if not (l1 or l2 or carry > 0):
        break

    runner.next = ListNode()
    runner = runner.next

これなら行けますが個人的にwhile Trueはエッジケースに引っかかった場合の永遠ループが怖くて実務では使いたくないですね。
ほかの方のrunner.nextに足した結果のノードを置いていく方式ならwhile文にl1 or l2 or carry > 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.

どっちも同値ではないですか?

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文の外にcan_create_next_node = l1 or l2 or carry > 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.

あ、「コメントのコードも元のコードも振る舞いは同じではないでしょうか」という意味でした。

変数に置いても、結局2箇所で更新が必要そうですね。

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 Trueという見た目で、無限ループを心配するのは違和感があります。

https://e-words.jp/w/%E7%84%A1%E9%99%90%E3%83%AB%E3%83%BC%E3%83%97.html

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 l1 or l2 or carry > 0に関してもl1, l2, carryが正しく更新されないと無限ループに入る可能性があるのでwhile Trueに対して無限ループに怯えるのは筋違いですね。ただ個人的にはwhile Trueよりwhile l1 or l2 or carry > 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.

同じ条件式を繰り返すのは、DRYに反するので、好ましくないかなと。while Trueの方なら、do whileに相当するので、すんなり読めそうです。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

次のループのノードを先に用意しておく必要がない、下のダミーノードの方法の方が総合的に優れているように感じました。

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.

なるほど、ノード関係の問題はdummyが役立ちそうな場面が多そうなので常に選択肢に持っておきます。

runner.next = ListNode()
runner = runner.next

return answer_head
```
これでacceptされた。空間計算量がO(n)かかり、l1やl2のメモリをそのまま活用する方法もあったと思うが分かりやすさを考えると別にこれでもいい気がする。
他の方のコードを読んでみる。

https://github.com/fhiyo/leetcode/pull/5/changes/f2e20f4d25a90b2fd41c44fc33005a620646d0fd
```cpp
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode dummy_head = ListNode(-1);
ListNode* node = &dummy_head;
int carry = 0;
while (l1 || l2 || carry) {
const int total = getValOrDefault(l1, 0) + getValOrDefault(l2, 0) + carry;
carry = total / 10;
node->next = new ListNode(total % 10); // digit
node = node->next;
l1 = getNextIfExists(l1);
l2 = getNextIfExists(l2);
}
return dummy_head.next;
}

private:
static int getValOrDefault(const ListNode* const node, const int default_value) {
if (!node) return default_value;
return node->val;
}

static ListNode* getNextIfExists(const ListNode* const node) {
if (!node) return nullptr;
return node->next;
}
};
```
だいたい一緒だった。自分のコードではrunnerにそのまま新しいNodeを代入し、while文の最後でrunner.nextにてNoneで終わるかNodeを作るかを判断していた。この方はdummy_headを使っていて、先頭のnode.nextに足し算結果のノードを代入しているので最後にnode.nextにNoneを入れるか次のノードを入れるか判断する必要がない。ただ個人的には自分の方の回答の方が納得するので自分の方ので行こうと思う。

## step2:
### code
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
carry_up = 0
answer_head = ListNode()
runner = answer_head
while l1 or l2 or carry_up > 0:
val_sum = 0
if l1:
val_sum += l1.val
l1 = l1.next
if l2:
val_sum += l2.val
l2 = l2.next
val_sum += carry_up

runner.val = val_sum % 10
carry_up = val_sum // 10
if l1 or l2 or carry_up > 0:
runner.next = ListNode()
runner = runner.next

return answer_head
```

## step3:
### code
```python
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
carry_up = 0
answer_head = ListNode()
runner = answer_head
while l1 or l2 or carry_up > 0:
val_sum = 0
if l1:
val_sum += l1.val
l1 = l1.next
if l2:
val_sum += l2.val
l2 = l2.next
val_sum += carry_up

runner.val = val_sum % 10
carry_up = val_sum // 10
if l1 or l2 or carry_up > 0:
runner.next = ListNode()
runner = runner.next

return answer_head
```