-
Notifications
You must be signed in to change notification settings - Fork 0
82_remove_duplicates_2 #4
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: 83_remove_duplicates
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,152 @@ | ||
| # Step 1 | ||
|
|
||
| ## 1回目 | ||
|
|
||
| ### コード | ||
|
|
||
| 二つポインタを作り、片方を `node_curr`、もう片方を `runner` として走らせて重複を検知した上で、最後に `node_curr` 自体も消してしまおうとした。うまく行かなかった。 | ||
|
|
||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode* node_curr = head; | ||
|
|
||
| while (node_curr != nullptr && node_curr->next != nullptr) { | ||
| if (node_curr->val == node_curr->next->val) { | ||
| // Use runner | ||
| ListNode* runner = node_curr; | ||
|
|
||
| // NOTE: この比較はおかしい | ||
| while (runner->val == runner->next->val) { | ||
| // Move the runner first? | ||
| runner = runner->next; | ||
| node_curr->next = node_curr->next->next; | ||
| } | ||
|
|
||
| node_curr = node_curr->next; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return head; | ||
| } | ||
| }; | ||
| ``` | ||
| ### メモ | ||
|
|
||
| - `runner`を使うのであれば `if (node_curr->val ...)` の比較は必要ない。 | ||
|
|
||
| - 初手で頭にアイデアが浮かんだので、紙にステップを書いて考えてみた。できなかったが、頭は整理される。 | ||
| 自然言語でやりたいことをちゃんと整理すると、上のコードの変な点が思い浮かぶ。 | ||
|
|
||
| - たとえば、「`runner` と比較するべきなのは何?」など。 | ||
|
|
||
| - 気になったので上のコードからさらに考えて `is_dup_curr` というフラッグを作って扱おうとした。方針としては良さそうだが、別の点でうまくいかない。 | ||
| ここで諦めて解答を見る。 | ||
|
|
||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode* node_curr = head; | ||
| bool is_dup_curr = false; | ||
|
|
||
| while (head != nullptr) { | ||
| node_curr = head; | ||
| while(node_curr != nullptr) { | ||
| ListNode* runner = node_curr; | ||
| is_dup_curr = false; // re-initialize | ||
|
|
||
| if (runner->next != nullptr && | ||
| runner->next->val == node_curr->val) { | ||
| runner->next = runner->next->next; | ||
|
|
||
| // flag | ||
| is_dup_curr = true; | ||
| } | ||
|
|
||
| node_curr = node_curr->next; | ||
| } | ||
|
|
||
| if (is_dup_curr == true) { | ||
| head = head->next; | ||
| } | ||
| } | ||
|
|
||
| return head; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| - 最終的に、dummy node を使うという発想がなかったことがわかった。 | ||
|
|
||
| ## 2回目 | ||
|
|
||
| コードは step1.cpp に書いた。 | ||
|
|
||
| ### メモ | ||
|
|
||
| - `ListNode* prev = &dummy` によって `prev` に加えられた変更が `dummy` にも加えられ続ける(アドレス参照)? | ||
|
|
||
| - 色々な input を使って 1ステップごとに考えてみた。変数の名付けに自分の認知が釣られていることがよくわかった。 | ||
|
|
||
| - `node_curr`が前に進んでも、前の `node_curr->next`はちゃんと残る?各回の if ... else の中の `node_curr->next = runner;` の処理結果は、次の if ... else にもそのまま保持されているということ? | ||
|
|
||
| - `node_curr` はポインタであってノードそのものではない。`node_curr->next`は「`node_curr`が指していたノードの`next`」を表している。 | ||
| こう考えるとしっくりくるが...。 | ||
|
|
||
| - とりあえず、こういう風な理解に到達した後はスラスラと書けた。 | ||
|
|
||
| # Step 2 | ||
|
|
||
| ## コード | ||
|
|
||
| 最終形を step2.cpp に書いた。 | ||
|
|
||
| ## 勉強 | ||
|
|
||
| - [他の人のコード](https://github.com/hemispherium/LeetCode_Arai60/pull/5/commits/a1e0bb0ed12ad3b03421cb10309fa8a8246d5e99)で `dummy(-1000)`を使っているのを見かけた。`-100 <= Node.val <= 100` という制約から外れるようにしているのだと理解した (参照:[このコメント](https://github.com/5103246/LeetCode_Arai60/pull/4/commits/a5301e9db863d53d1d6c4e9925cff2917555e970#r2287948164))。 | ||
|
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. (リンク先にもコメントしましたが、)私は、個人的には意味のありそうな -1000 はあまり好まないですね。読んだ人は-1000というのはどういう事情で決まったのだろうかと思うはずです。 デバッグでは 0xDEADBEEF などで埋めるという慣習もあります。 |
||
|
|
||
| - [このコメント](https://github.com/5103246/LeetCode_Arai60/pull/4/commits/a5301e9db863d53d1d6c4e9925cff2917555e970#r2286491895)を見る。重複のあるノードを飛ばした後にそのノードのために使用していたメモリを解放するという作業が存在することがわかった。ノードは heap に置かれているので、ちゃんと削除してあげると良い。 | ||
|
|
||
| - [ここ](https://discord.com/channels/1084280443945353267/1366778718705553520/1377689037208289471)でいうデザインの話なのか? | ||
|
|
||
| - `delete` を使ってみる。 | ||
|
|
||
| - 名付けの問題が色々指摘されている。 | ||
|
|
||
| - [Google Style Guide](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules)より。`node_curr`は`node_current`として良いかも。同様に `val` → `val_duplicate`。 | ||
|
|
||
| - ネストが深すぎるかも?たとえば次のコードと比べてどちらが見やすいだろうか。ただ、判断がつかないのでとりあえずはこのまま。 | ||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode dummy; | ||
| dummy.next = head; | ||
|
|
||
| ListNode* prev = &dummy; | ||
| ListNode* curr = head; | ||
| while (curr) { | ||
| while (curr->next && curr->val == curr->next->val) { | ||
| curr = curr->next; | ||
| } | ||
|
|
||
| if (prev->next != curr) { | ||
| prev->next = curr->next; | ||
| } else { | ||
| prev = curr; | ||
| } | ||
|
|
||
| curr = curr->next; | ||
| } | ||
|
|
||
| return dummy.next; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| # Step 3 | ||
|
|
||
| スムーズに書けた。 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode dummy(0); | ||
| dummy.next = head; | ||
|
|
||
| ListNode* node_curr = &dummy; | ||
| ListNode* runner = head; | ||
|
|
||
| while (runner != nullptr && runner->next != nullptr){ | ||
| if (runner->val == runner->next->val) { | ||
| // flag | ||
| int val_dup = runner->val; | ||
|
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. ありがとうございます。コメントとスタイルガイドを確認しました。 |
||
|
|
||
| // ensures runner is past the duplicated block | ||
| while (runner->val == val_dup) { | ||
| runner = runner->next; | ||
| } | ||
|
|
||
| node_curr->next = runner; | ||
|
|
||
| } else { | ||
| // detach node_curr from dummy.next | ||
| node_curr = runner; | ||
| runner = runner->next; | ||
| } | ||
| } | ||
|
|
||
| return dummy.next; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode dummy(-1000); | ||
| dummy.next = head; | ||
|
|
||
| ListNode* node_current = &dummy; | ||
| ListNode* runner = head; | ||
|
|
||
| while (runner != nullptr && runner->next != nullptr){ | ||
| if (runner->val == runner->next->val) { | ||
| // flag | ||
| int val_duplicate = runner->val; | ||
|
|
||
| // ensures runner is past the duplicated block | ||
| while (runner->val == val_duplicate) { | ||
|
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.
|
||
| ListNode* node_duplicate = runner; | ||
| runner = runner->next; | ||
| delete node_duplicate; | ||
| } | ||
|
|
||
| node_current->next = runner; | ||
|
|
||
| } else { | ||
| node_current = runner; | ||
| runner = runner->next; | ||
| } | ||
| } | ||
|
|
||
| return dummy.next; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| class Solution { | ||
| public: | ||
| ListNode* deleteDuplicates(ListNode* head) { | ||
| ListNode dummy(-1000); | ||
| dummy.next = head; | ||
|
|
||
| ListNode* node_current = &dummy; | ||
| ListNode* runner = head; | ||
|
|
||
| while (runner != nullptr && runner->next != nullptr) { | ||
| if (runner->val == runner->next->val) { | ||
| int val_duplicate = runner->val; | ||
|
|
||
| while (runner->val == val_duplicate) { | ||
| ListNode* node_duplicate = runner; | ||
| runner = runner->next; | ||
| delete node_duplicate; | ||
| } | ||
|
|
||
| node_current->next = runner; | ||
|
|
||
| } else { | ||
| node_current = runner; | ||
| runner = runner->next; | ||
| } | ||
| } | ||
|
|
||
| return dummy.next; | ||
| } | ||
| }; |
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.
アドレス参照という言葉は C++ にはないと思います。
prev には dummy へのポインターが格納されます。 prev が dummy の実体 (インスタンス) を指し示している状態です。この状態で prev が指し示しているものを変更すると、 prev が指し示している dummy の実体が変更される、という状況です。
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.
ありがとうございます。言葉の意味をよく分からずに書いていました。
2段落目の説明を読んでより理解が深まりました。