From 31eb7d7b7863abc1dfd8233bce9a814d09ad9e7c Mon Sep 17 00:00:00 2001 From: Taito Ohsumi Date: Tue, 26 Nov 2024 12:45:22 +1100 Subject: [PATCH 1/3] Add 2. Add Two Numbers.md --- 2. Add Two Numbers.md | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 2. Add Two Numbers.md diff --git a/2. Add Two Numbers.md b/2. Add Two Numbers.md new file mode 100644 index 0000000..042a5de --- /dev/null +++ b/2. Add Two Numbers.md @@ -0,0 +1,125 @@ +# step 1 +下の桁から足していく足し算をする問題 +```python3 +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + node1 = l1 + node2 = l2 + dummy_head = ListNode() + sum_node = dummy_head + carry = 0 + while node1 is not None or node2 is not None: + value1 = 0 + value2 = 0 + if node1 is not None: + value1 = node1.val + node1 = node1.next + if node2 is not None: + value2 = node2.val + node2 = node2.next + + sum_value = (value1 + value2 + carry) % 10 + carry = (value1 + value2 + carry) // 10 + sum_node.next = ListNode(val=sum_value) + sum_node = sum_node.next + + if carry == 1: + sum_node.next = ListNode(val=1) + + return dummy_head.next +``` + +修正可能箇所 +- whileのすぐ下のvalue1, value2は三項演算子を使うと、行数短縮できる。 +pythonだとTrue時の値が最初にくる()ので、読みにくいように思う。 +- `node1`,`node2`という変数名に対して`sum_node`とnodeという単語が後ろに置かれている。 +加算の結果だというニュアンスを出したかったので、sumを前にしたが、可読性は変わらなさそう。 +- `node* is not None`の処理の重複:ループを使いながらこの重複なしで書こうとすると、 +`while node1 is not None and node2 is not None`の後に、 +`while node1 is not None`と`while node2 is not None`をぶら下げることになりそう。 + +n、mをl1, l2のノード数として +- time complexity: O(max(n, m)) +- space complexity: O(max(n, m)) + +# step 2 +参考 +- https://github.com/philip82148/leetcode-arai60/pull/2 何も知らない人が上から読んでいってパズルができるだけ少ないように書く +- https://github.com/katataku/leetcode/pull/4 入力リストから値を取る関数を用意していた。それによって、値を取る処理とリストを進める処理が別になっていてわかりやすい。sentinelなしでも書けるっぽいがheadの値がループ中で決められており、若干読みにくかった。 +- https://github.com/konnysh/arai60/pull/5/files +- 再帰で書いているパターンもあった。dummy_headのメモリ確保が気になるタイプの言語を使っている方に多い印象。 +- `if carry == 1:`のチェックもwhileの中に入れている回答があった。 +たしかに、「`l1`のn桁目の値(無ければ0)と`l2`のn桁目の値(無ければ0)と繰り上がりの値の和の1の位が +和のn桁目になります。全てが0になった時、加算終了です」の方が、最後の桁チェックだけ別でやるよりも自然。 +- step 1では`sum_value = (value1 + value2 + carry) % 10`としていたが、変数名と意味が合っていない。 +- https://peps.python.org/pep-0308/ : pythonの三項演算子が導入される過程、なんであの構文になったのかという話。 +既存のコードを壊さないように機能を追加するトレードオフを考えながら、どういう構文がいいか・いらないかみたいな話をしていて、まさしくエンジニアリングなのかなぁと思った。 + +```python3 +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + def get_value(node): + if node is not None: + return node.val + return 0 + + node1 = l1 + node2 = l2 + carry = 0 + dummy_head = ListNode() + node_sum = dummy_head + while node1 is not None or node2 is not None or carry != 0: + value1 = get_value(node1) + value2 = get_value(node2) + value_sum = value1 + value2 + carry + + digit = value_sum % 10 + carry = value_sum // 10 + node_sum.next = ListNode(val=digit) + + node_sum = node_sum.next + if node1 is not None: + node1 = node1.next + if node2 is not None: + node2 = node2.next + return dummy_head.next +``` +# step 3 +ノードから値を取る処理、次のノードを取る処理は分けられたので分けることにした。 + +可能ならListNodeをいじって、`node.get_value(default=0)`とかいうメソッドを追加したい。 + +コーディング面接でテンパらずにここまで処理をまとめられるようになるのかはよくわからない。 +慣れればできるんだろうか。 + +```python3 +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + def get_value(node): + if node is not None: + return node.val + return 0 + def get_next(node): + if node is not None: + return node.next + return None + + node1 = l1 + node2 = l2 + carry = 0 + dummy_head = ListNode() + node_sum = dummy_head + while node1 is not None or node2 is not None or carry != 0: + value1 = get_value(node1) + value2 = get_value(node2) + value_sum = value1 + value2 + carry + + digit = value_sum % 10 + carry = value_sum // 10 + node_sum.next = ListNode(val=digit) + + node_sum = get_next(node_sum) + node1 = get_next(node1) + node2 = get_next(node2) + return dummy_head.next +``` \ No newline at end of file From 20617d2a737d4b658a934830d12dcc719057a8c5 Mon Sep 17 00:00:00 2001 From: Taito Ohsumi Date: Wed, 4 Dec 2024 13:53:49 +1100 Subject: [PATCH 2/3] Add step 4 --- 2. Add Two Numbers.md | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/2. Add Two Numbers.md b/2. Add Two Numbers.md index 042a5de..8e613ed 100644 --- a/2. Add Two Numbers.md +++ b/2. Add Two Numbers.md @@ -122,4 +122,59 @@ class Solution: node1 = get_next(node1) node2 = get_next(node2) return dummy_head.next +``` + +# step 4 +コメントまとめ +- [divmod](https://docs.python.org/3/library/functions.html#divmod) +- [インスタンスにメソッドを追加する](https://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance-in-python) +- [クラスを後からいじる](https://discord.com/channels/1084280443945353267/1225849404037009609/1228375055717765190) +- ノードを動かすタイミングで二つの意見をもらった + - n桁目の値を取り出したらすぐにノードを進める方が読みやすい + - n桁目の合計を計算する前に、ノードを進めるのは読みにくい + + 前者はノードに注目をしていて、後者は全体のcomputationに注目をしている気がした。 + 足し算の各桁での処理は雑な言語化をすれば、 + 1. n桁目の値を取ってくる。なければ0 + 2. n-1桁目の繰り上がりと、n桁目の値を足す + 3. 加算結果の値から、n桁目の計算結果と、繰り上がりを求める + 4. 次の桁にうつる + + というような処理をしているので、個人的な好みは後者だった。 + +```python +class Solution: + def addTwoNumbers( + self, + l1: Optional[ListNode], + l2: Optional[ListNode] + ) -> Optional[ListNode]: + def get_value(node): + if node is not None: + return node.val + return 0 + def get_next(node): + if node is not None: + return node.next + return None + + dummy_head = ListNode() + node = dummy_head + node1 = l1 + node2 = l2 + carry = 0 + + while node1 is not None or node2 is not None or carry > 0: + value1 = get_value(node1) + value2 = get_value(node2) + value_sum = value1 + value2 + carry + + carry, digit = divmod(value_sum, 10) + node.next = ListNode(val=digit) + + node = node.next + node1 = get_next(node1) + node2 = get_next(node2) + + return dummy_head.next ``` \ No newline at end of file From 9b5cde719c77daf2b2fd00ccd5f14b82b15420c0 Mon Sep 17 00:00:00 2001 From: Taito Ohsumi Date: Mon, 10 Feb 2025 23:04:01 +1100 Subject: [PATCH 3/3] Add other solutions --- 2. Add Two Numbers.md | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/2. Add Two Numbers.md b/2. Add Two Numbers.md index 8e613ed..b96ab31 100644 --- a/2. Add Two Numbers.md +++ b/2. Add Two Numbers.md @@ -177,4 +177,67 @@ class Solution: node2 = get_next(node2) return dummy_head.next +``` + +dummy headなし +```python +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + def get_val(node: Optional[ListNode]) -> int: + if node is not None: + return node.val + return 0 + + def get_next(node: Optional[ListNode]) -> Optional[ListNode]: + if node is not None: + return node.next + return None + + value = get_val(l1) + get_val(l2) + carry, digit = divmod(value, 10) + head = ListNode(val=digit) + l1 = get_next(l1) + l2 = get_next(l2) + tail = head + while l1 is not None or l2 is not None or carry > 0: + value = get_val(l1) + get_val(l2) + carry + carry, digit = divmod(value, 10) + tail.next = ListNode(val=digit) + tail = tail.next + l1 = get_next(l1) + l2 = get_next(l2) + return head +``` + +再帰 +```python +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + def get_val(node: Optional[ListNode]) -> int: + if node is not None: + return node.val + return 0 + def get_next(node: Optional[ListNode]) -> Optional[ListNode]: + if node is not None: + return node.next + return None + + 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 + + value = carry + get_val(l1) + get_val(l2) + carry, digit = divmod(value, 10) + head = ListNode(val=digit) + head.next = add_two_numbers_helper( + get_next(l1), + get_next(l2), + carry + ) + return head + return add_two_numbers_helper(l1, l2, 0) ``` \ No newline at end of file