diff --git a/arai60/add-two-numbers/README-en.md b/arai60/add-two-numbers/README-en.md new file mode 100644 index 0000000..ef43566 --- /dev/null +++ b/arai60/add-two-numbers/README-en.md @@ -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 diff --git a/arai60/add-two-numbers/README.md b/arai60/add-two-numbers/README.md new file mode 100644 index 0000000..6fa70f6 --- /dev/null +++ b/arai60/add-two-numbers/README.md @@ -0,0 +1,29 @@ +## 考察 +- 過去に解いたことあり +- 方針 + - 繰り上がりを保持しながら、対応するノードを一つずつ足していけばよさそう(頭の中では筆算を連想) + - time: O(n), space: O(n) + - 繰り上がりが最後に残った場合のみ注意 +- リストの走査は繰り返しでも再帰でもOK + - 再帰にするメリットは思いつかないので繰り返しでいく +- あとは実装 + +## Step1 +- 上で考えたアルゴリズムを実装 +- 1つ気になるのがメモリ管理のところ + - 新しいノードをスタック領域に確保するとスコープを抜けた時点でメモリが解放されるのでうまくいかない + - ヒープ領域にオブジェクトを作成したけど、このオブジェクトの管理は誰の責務になるのかがわかっていない + - C++実務で使ったことないが、実務だとどうやるんだろう + - Linked Listのポインタをスマートポインタにすればよさそう? -> 参照が切れた時点で自動的にメモリが解放されそう + +## Step2 +- メモリ管理について他の人のPRをいくつか検索してみた + - LeetCode上の制限という問題もありそう。ただ問題意識は常に持っていたい。 + - Ref. https://github.com/Ryotaro25/leetcode_first60/pull/5#discussion_r1611301775 +- 最後にcarryをチェックして、1を追加していたところをメインロジックに吸収できたので修正した + +## Step3 +- 1回目: 2m00s +- 2回目: 1m58s +- 3回目: 1m33s + diff --git a/arai60/add-two-numbers/step1.cpp b/arai60/add-two-numbers/step1.cpp new file mode 100644 index 0000000..68a4179 --- /dev/null +++ b/arai60/add-two-numbers/step1.cpp @@ -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) { + 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; + } +}; diff --git a/arai60/add-two-numbers/step1_recursion.cpp b/arai60/add-two-numbers/step1_recursion.cpp new file mode 100644 index 0000000..9ceff6c --- /dev/null +++ b/arai60/add-two-numbers/step1_recursion.cpp @@ -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); + } +}; diff --git a/arai60/add-two-numbers/step2.cpp b/arai60/add-two-numbers/step2.cpp new file mode 100644 index 0000000..29a4841 --- /dev/null +++ b/arai60/add-two-numbers/step2.cpp @@ -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; + } +}; diff --git a/arai60/add-two-numbers/step3.cpp b/arai60/add-two-numbers/step3.cpp new file mode 100644 index 0000000..06c3d30 --- /dev/null +++ b/arai60/add-two-numbers/step3.cpp @@ -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; + 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; + } +}; diff --git a/arai60/add-two-numbers/step3_without_dummy.cpp b/arai60/add-two-numbers/step3_without_dummy.cpp new file mode 100644 index 0000000..15380ee --- /dev/null +++ b/arai60/add-two-numbers/step3_without_dummy.cpp @@ -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; + } +};