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..f110fe3 --- /dev/null +++ b/82. Remove Duplicates from Sorted List II.md @@ -0,0 +1,149 @@ + +URL: https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/description/ + +# Step 1 + +- 実装時間: 15+3分 +- 時間計算量: O(n) +- 空間計算量: O(1) +- 問題を見て、「ループで実装する方法」と「再帰で実装する方法」の二種類を思いついた。ループで解いてみたが15分で解けなかったので、再帰で実装。 + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None: + return None + if head.next is None: + return head + if head.val != head.next.val: + head.next = self.deleteDuplicates(head.next) + return head + target_val = head.val + node = head + while node is not None and node.val == target_val: + node = node.next + return self.deleteDuplicates(node) +``` + +↓が時間切れで解き切れなかったループの残骸。入力`[1,1]`でエラーになる。 + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None: + return None + node = head + dummy = ListNode(0, head) + pre = dummy + while node is not None: + if node.next is not None and node.val == node.next.val: + target_val = node.val + while node is not None and node.val == target_val: + node = node.next + continue + pre.next = node + pre = node + node = node.next + return dummy.next +``` + +# Step 2 + +- 再帰はオーバーヘッドがあるので避けるべきだと思っていたが、別の観点もある。 + - 再帰でない記述だと入力にループがある場合無限ループになるのに対して、再帰で書くと`RecursionError`を投げてくれるのでこちらのほうがよいという考えもありますね。 +https://docs.python.org/3/library/exceptions.html#RecursionError + +- 命名について + - `sentinel`という命名があることを知った。 + - `target_val`も意味のあまりない命名だと気づいた。`value_to_remove`を使う。 + - `val`もvalueの略語なので、避ける。 + +- ループでの実装について。 + - 「重複していて取り除く」のか「重複していないから取り除かない」のかの二種類の状態がある。 + - 「重複していないから取り除かない」でearly returnするとみやすい。 + - `pre`と`node`の2変数は不要。1つの変数で管理できる。 + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None: + return None + dummy = ListNode(0, head) + node = dummy + while node.next is not None: + if node.next.next is None or node.next.val != node.next.next.val: + node = node.next + continue + value_to_remove = node.next.val + while node.next is not None and node.next.val == value_to_remove: + node.next = node.next.next + return dummy.next +``` + +- `is_duplicated`などのフラグを使うと、whileループのネストを避けることができ、O(n)ということが伝わりやすい。 + - `Optional[int]`という方法も。 + - https://discord.com/channels/1084280443945353267/1226508154833993788/1246022270984392724 + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(0, head) + node = dummy + deleting_number = None + while node.next is not None: + if deleting_number is not None and node.next.val == deleting_number: + node.next = node.next.next + continue + if node.next.next is not None and node.next.val == node.next.next.val: + deleting_number = node.next.val + continue + deleting_number = None + node = node.next + + return dummy.next +``` + +# Step 3 + +- 時間計算量: O(n) +- 空間計算量: O(1) + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + sentinel = ListNode(0, head) + node = sentinel + deleting_number = None + while node.next is not None: + if deleting_number is not None and node.next.val == deleting_number: + node.next = node.next.next + continue + if node.next.next is not None and node.next.val == node.next.next.val: + deleting_number = node.next.val + continue + deleting_number = None + node = node.next + return sentinel.next +``` + +# Step 4 + +preを使った書き方についてコメントをいただいたので、一晩おいた後に書いてみた。 + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + sentinel = ListNode(0, head) + pre = sentinel + node = head + while node is not None: + if node.next is None or node.val != node.next.val: + pre = pre.next + node = node.next + continue + value_to_remove = node.val + while node is not None and node.val == value_to_remove: + node = node.next + pre.next = node + return sentinel.next +```