diff --git a/82. Remove Duplicates from Sorted List II.md b/82. Remove Duplicates from Sorted List II.md new file mode 100644 index 0000000..6175096 --- /dev/null +++ b/82. Remove Duplicates from Sorted List II.md @@ -0,0 +1,100 @@ +# step1 +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummyHead = ListNode(0) + dummyHead.next = head + current = dummyHead + while current.next and current.next.next: + if current.next.val == current.next.next.val: + copy = current.next + while copy.next and copy.val == copy.next.val: + copy = copy.next + current.next = copy.next + else: + current = current.next + return dummyHead.next +``` +#### 思考ログ +- 前回の問題では現在のノードと次のノードを見比べたので、今回は次のノードのその次のノードを見ればいいのではと思ったが、2連で同じノードが続いている場合しか対応できないことに気づいた +- 再帰とか使えば書けるのかなと思っていたら5分経っていたのでaraiさんの解説を視聴 +- 解説を見たら理解できたが、なかなか0から書ける様にならないので自然言語で整理してみる + - 1. 現在ノードを基準とし、1つ先のノードとその次のノードが一致しているか確認する + - 2. 一致している場合、それがどこまで続くのかを確認し、同じ値の最後のノードの次のノードを現在ノードの次のノードに設定する。 + - 3. 一致していない場合、現在ノードを進める + - 4. 1番目から連続している場合、一番最初にダミーノードを作成しそこを基準に判定する。returnには含めない。 +- この思考の順番であっているのか、1番目から連続しているパターンを最初から考えるのか後から考えるのか。個人的には基本的な開放を考えてからエッジパターンを拾うという思考の流れの方が自然な気がする。 +- なかなかにわかりにくい文章だが、頭の中で整理できたようで5分で回答が書けるようになった。 + +# step2 +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(1) + dummy.next = head + node = dummy + while node.next and node.next.next: + if node.next.val != node.next.next.val: + node = node.next + continue + duplicate = node.next + while duplicate.next and duplicate.val == duplicate.next.val: + duplicate = duplicate.next + node.next = duplicate.next + return dummy.next +``` +#### 思考ログ +- step1では新井さんの解説動画の変数名をそのまま使ったが、copyなどの変数名は前回のレビューで指摘された通り汎用的すぎるので別の名前を考える。 + - copy → duplicate +- 言語化した文章をもう少し推敲してみる + - 1. 現在ノードを基準とし、次とその次のノードの値が一致しているかをみる + - 2. 一致していない場合は現在ノードを次のノードに進める。 + - 3. 一致している場合はどこまで一致しているかを調べる。 + - 4. 一致しなくなったペアの後ろのノードを現在ノードの次のノードに付け替える + - 5. 一つ目のノードから連続している場合は↑の方法が取れないため、一つ目のノードの前にノードを作成する。 + - 6. 一つ目のノードは自分で作ったノードなので、そのノードは飛ばしてreturnする。 +- 言語化したらできるようになったことについて、それっぽい話があった + - > 「自分で手作業でできる」「人間にやり方を説明して代わりにやってもらえる」「機械にやり方を説明して代わりにやってもらえる(おおまかに、これがコードが書けること)」の順で難しくなっていくので、より簡単なのができないのだとたぶん書けないのです。https://github.com/atomina1/Arai60_review/pull/3#r1893847805 +- > duplicateがある場合とない場合2つに分けられ、また、片方だけ一行で済む様な(考え方として)軽い処理の時に、軽い処理を先に終わらせてしまったほうが、あまり頭を使わない気がします。https://github.com/atomina1/Arai60_review/pull/3#r1893546879 + - 確かにとおもったので、ifとelseを逆にする +- Sentinel valueというものがあるそう + - draftだがpepでもこれに関する記載があった(https://peps.python.org/pep-0661/) + - dummyHeadをsentinelという名前にできるかなとも思ったがwikiを見る感じ終了を表すために使われるそうなので用途が違いそう + - https://ja.wikipedia.org/wiki/%E7%95%AA%E5%85%B5#%E7%B5%82%E4%BA%86%E3%82%92%E8%A1%A8%E3%81%99%E3%81%9F%E3%82%81%E3%81%AE%E5%B0%82%E7%94%A8%E3%81%AE%E5%80%A4 + - と思ったが、leetCodeの解答例にsentinelが使われているので使っても良さそう +- 命名はgoogleのstyle guideに習った方が良さそうなのでそちらに倣う https://github.com/t0hsumi/leetcode/pull/4/files#r1871480436 + - 関数名をいじるとleetCodeで動かなくなるので関数名はそのままでいく +- >で、私の指摘はなんだったかというと、current_value は読んでいる人にとって、ほとんど情報がないです。「現在の値」ですよね。日本語に直すときに「この状態での合計」に直したわけですが、そうだとすると「合計」sum で、少なくとも、current_sum だと、合計である事が分かります。ただ、current も本当は変えたくて、というのも「今」注目しているくらいしか意味がないです。sum_so_far とかなんかあるでしょう。 https://discord.com/channels/1084280443945353267/1225849404037009609/1234206158630289450 + - というレスがあった、本コードでもcurrentである必要性を感じないのでnodeとしても良さそう + - dummy_headもheadであるというのはあまり意味を持たないので、dummyだけで良さそうなのでリファクタする +- 1重でもかけるそう + - それぞれの違いは以下 + - 1重:重複を発見したら、ノードを見比べて一つ一つノードを削除する。 + - 次のループへの引き継ぎのため変数を多く用意する必要があり、メモリ使用量が多くなる。(といっても全ノードに対して変数を用意するわけではないのでO(1)) + - 2重:重複を発見したら、重複が存在する全てのノードを一気に削除する。 + - ループが二重になるのでtime complexityが増える。 + - とおもったが、二重目の方は全て見るわけではないのでO(N)になるらしいい。 + - 両方とも計算量は変わらない + - time complexity:O(N) + - space complexity:O(1) + +# step3 +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(1) + dummy.next = head + node = dummy + while node.next and node.next.next: + if node.next.val != node.next.next.val: + node = node.next + continue + duplicate = node.next + while duplicate.next and duplicate.val == duplicate.next.val: + duplicate = duplicate.next + node.next = duplicate.next + return dummy.next +``` +#### 思考ログ +- 自然言語で整理する工程を入れたことで、記憶しやすくなったからか一発で3連続回答できた。 +- 時間も短くなった。