-
Notifications
You must be signed in to change notification settings - Fork 0
2. Add Two Numbers #6
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,28 @@ | ||
| ## Considerations | ||
| - Solved this problem in the past | ||
| - Approach | ||
| - Keep track of carry while adding corresponding nodes one by one (similar to manual addition in your head) | ||
| - time: O(n), space: O(n) | ||
| - Pay special attention if a carry remains at the end | ||
| - Traversing the list can be done iteratively or recursively | ||
| - Can't think of any benefits for recursion, so will go with iteration | ||
| - Now to implement | ||
|
|
||
| ## Step 1 | ||
| - Implemented the algorithm considered above | ||
| - One concern is memory management | ||
| - Allocating new nodes on the stack won't work as memory is released when out of scope | ||
| - Created objects in the heap, but unsure who is responsible for managing these objects | ||
| - Haven't used C++ in a professional setting, but how is this handled in practice? | ||
| - Would using smart pointers for Linked List pointers help? -> Memory should be automatically released when references are gone | ||
|
|
||
| ## Step 2 | ||
| - Searched other people's PRs regarding memory management | ||
| - There might be limitations specific to LeetCode, but it's important to be aware of this issue | ||
| - Ref. https://github.com/Ryotaro25/leetcode_first60/pull/5#discussion_r1611301775 | ||
| - Fixed by incorporating the final carry check into the main logic | ||
|
|
||
| ## Step 3 | ||
| - First time: 2m00s | ||
| - Second time: 1m58s | ||
| - Third time: 1m33s |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| ## 考察 | ||
| - 過去に解いたことあり | ||
| - 方針 | ||
| - 繰り上がりを保持しながら、対応するノードを一つずつ足していけばよさそう(頭の中では筆算を連想) | ||
| - time: O(n), space: O(n) | ||
| - 繰り上がりが最後に残った場合のみ注意 | ||
| - リストの走査は繰り返しでも再帰でもOK | ||
| - 再帰にするメリットは思いつかないので繰り返しでいく | ||
| - あとは実装 | ||
|
|
||
| ## Step1 | ||
| - 上で考えたアルゴリズムを実装 | ||
| - 1つ気になるのがメモリ管理のところ | ||
| - 新しいノードをスタック領域に確保するとスコープを抜けた時点でメモリが解放されるのでうまくいかない | ||
| - ヒープ領域にオブジェクトを作成したけど、このオブジェクトの管理は誰の責務になるのかがわかっていない | ||
| - C++実務で使ったことないが、実務だとどうやるんだろう | ||
| - Linked Listのポインタをスマートポインタにすればよさそう? -> 参照が切れた時点で自動的にメモリが解放されそう | ||
|
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.
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. ありがとうございます。理解が深まりました。
|
||
|
|
||
| ## Step2 | ||
| - メモリ管理について他の人のPRをいくつか検索してみた | ||
| - LeetCode上の制限という問題もありそう。ただ問題意識は常に持っていたい。 | ||
| - Ref. https://github.com/Ryotaro25/leetcode_first60/pull/5#discussion_r1611301775 | ||
| - 最後にcarryをチェックして、1を追加していたところをメインロジックに吸収できたので修正した | ||
|
|
||
| ## Step3 | ||
| - 1回目: 2m00s | ||
| - 2回目: 1m58s | ||
| - 3回目: 1m33s | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /** | ||
| * Definition for singly-linked list. | ||
| * struct ListNode { | ||
| * int val; | ||
| * ListNode *next; | ||
| * ListNode() : val(0), next(nullptr) {} | ||
| * ListNode(int x) : val(x), next(nullptr) {} | ||
| * ListNode(int x, ListNode *next) : val(x), next(next) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| ListNode* addTwoNumbers(ListNode* list1, ListNode* list2) { | ||
| int carry = 0; | ||
| ListNode dummy_head; | ||
| ListNode* node = &dummy_head; | ||
| while (list2 || list1) { | ||
|
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 (list1 || list2)の方が違和感ないと思いました🙇
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. 気付きませんでした。ご指摘ありがとうございます。 |
||
| int sum = carry; | ||
| if (list1) { | ||
| sum += list1->val; | ||
| list1 = list1->next; | ||
| } | ||
| if (list2) { | ||
| sum += list2->val; | ||
| list2 = list2->next; | ||
| } | ||
|
|
||
| carry = sum / 10; | ||
| node->next = new ListNode(sum % 10); | ||
| node = node->next; | ||
| } | ||
|
|
||
| if (carry == 1) { | ||
| node->next = new ListNode(1); | ||
| } | ||
|
|
||
| return dummy_head.next; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /** | ||
| * Definition for singly-linked list. | ||
| * struct ListNode { | ||
| * int val; | ||
| * ListNode *next; | ||
| * ListNode() : val(0), next(nullptr) {} | ||
| * ListNode(int x) : val(x), next(nullptr) {} | ||
| * ListNode(int x, ListNode *next) : val(x), next(next) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| ListNode* addTwoNumbers(ListNode* list1, ListNode* list2) { | ||
| ListNode dummy_head; | ||
| ListNode* node = &dummy_head; | ||
| int carry = 0; | ||
|
|
||
| add(list1, list2, carry, node); | ||
|
|
||
| return dummy_head.next; | ||
| } | ||
|
|
||
| private: | ||
| void add(ListNode* list1, ListNode* list2, int carry, ListNode* node) { | ||
| if (!list1 && !list2 && !carry) return; | ||
|
|
||
| int sum = carry; | ||
| if (list1) { | ||
| sum += list1->val; | ||
| list1 = list1->next; | ||
| } | ||
| if (list2) { | ||
| sum += list2->val; | ||
| list2 = list2->next; | ||
| } | ||
|
|
||
| carry = sum / 10; | ||
| node->next = new ListNode(sum % 10); | ||
| node = node->next; | ||
|
|
||
| add(list1, list2, carry, node); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * Definition for singly-linked list. | ||
| * struct ListNode { | ||
| * int val; | ||
| * ListNode *next; | ||
| * ListNode() : val(0), next(nullptr) {} | ||
| * ListNode(int x) : val(x), next(nullptr) {} | ||
| * ListNode(int x, ListNode *next) : val(x), next(next) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| ListNode* addTwoNumbers(ListNode* list1, ListNode* list2) { | ||
| int carry = 0; | ||
| ListNode dummy_head; | ||
| ListNode* node = &dummy_head; | ||
| while (list1 || list2 || carry) { | ||
| int sum = carry; | ||
| if (list1) { | ||
| sum += list1->val; | ||
| list1 = list1->next; | ||
| } | ||
| if (list2) { | ||
| sum += list2->val; | ||
| list2 = list2->next; | ||
| } | ||
|
|
||
| carry = sum / 10; | ||
| node->next = new ListNode(sum % 10); | ||
| node = node->next; | ||
| } | ||
|
|
||
| return dummy_head.next; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * Definition for singly-linked list. | ||
| * struct ListNode { | ||
| * int val; | ||
| * ListNode *next; | ||
| * ListNode() : val(0), next(nullptr) {} | ||
| * ListNode(int x) : val(x), next(nullptr) {} | ||
| * ListNode(int x, ListNode *next) : val(x), next(next) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| ListNode* addTwoNumbers(ListNode* list1, ListNode* list2) { | ||
| ListNode dummy_head; | ||
|
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. 一応、dummy を使わない方法も書いてみますか?
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. dummyを使わない方法も書いてみました。-> a9c7637 |
||
| ListNode* node = &dummy_head; | ||
| int carry = 0; | ||
| while (list1 || list2 || carry) { | ||
| int sum = carry; | ||
| if (list1) { | ||
| sum += list1->val; | ||
| list1 = list1->next; | ||
| } | ||
| if (list2) { | ||
| sum += list2->val; | ||
| list2 = list2->next; | ||
| } | ||
|
|
||
| carry = sum / 10; | ||
| node->next = new ListNode(sum % 10); | ||
| node = node->next; | ||
| } | ||
|
|
||
| return dummy_head.next; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /** | ||
| * Definition for singly-linked list. | ||
| * struct ListNode { | ||
| * int val; | ||
| * ListNode *next; | ||
| * ListNode() : val(0), next(nullptr) {} | ||
| * ListNode(int x) : val(x), next(nullptr) {} | ||
| * ListNode(int x, ListNode *next) : val(x), next(next) {} | ||
| * }; | ||
| */ | ||
| class Solution { | ||
| public: | ||
| ListNode* addTwoNumbers(ListNode* list1, ListNode* list2) { | ||
| ListNode* head = nullptr; | ||
| ListNode* node = nullptr; | ||
| int carry = 0; | ||
| while (list1 || list2 || carry) { | ||
| int sum = carry; | ||
| if (list1) { | ||
| sum += list1->val; | ||
| list1 = list1->next; | ||
| } | ||
| if (list2) { | ||
| sum += list2->val; | ||
| list2 = list2->next; | ||
| } | ||
|
|
||
| carry = sum / 10; | ||
| if (!head) { | ||
| head = new ListNode(sum % 10); | ||
| node = head; | ||
| continue; | ||
| } | ||
| node->next = new ListNode(sum % 10); | ||
| node = node->next; | ||
| } | ||
|
|
||
| return head; | ||
| } | ||
| }; |
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.
再帰に関しても時間/空間計算量やコードに落とす際の観点も踏まえた方が
なぜ @kazukiii さんが今回の実装を選ばれたのかがより伝わると思いました。
よく自分が受けている指摘ですが🙇
Uh oh!
There was an error while loading. Please reload this page.
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.
レビューありがとうございます。なぜ選択したのかも言語化して残すようにしてみます。
以下、今回の問題に関して考えることです。
再帰に関しては実装方法の違いだと考えており、時間/空間計算量は同じになります。
再帰処理に関しては、再帰的な関数呼び出しや呼び出しごとに生成されるスタックフレームを考えると、時間的にも空間的にもオーバーヘッドが多少あるため、今回繰り返しを選択しました。
一応再帰でも書いてみました->c9ba9b4