-
Notifications
You must be signed in to change notification settings - Fork 0
82. Remove Duplicates from Sorted List II #3
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,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 | ||
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. pre.next について、ループを回している間、どのようなものであると思っているのかが、一定していないように見受けられます。
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. ありがとうございます。まさに、その二つは一定にできておらず、意識もできてませんでした。 nodchipさんのコメントの方に、内省など書きました。 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. これはどちら側に合わせても書けるはずなので、書いてみて比較して比べてみるといいと思います。 よく、私は、ループを仕事の引き継ぎで例えているんです。 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. Discord 内で「引き継ぎ」の話を結構しているので、よければ調べておいてください。 |
||
``` | ||
|
||
# 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 | ||
Comment on lines
+144
to
+146
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. この部分はヘルパー関数に切り出しても良いかもですね。 |
||
pre.next = node | ||
return sentinel.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.
エラーになった原因は理解していますか?
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.
コメントありがとうございます。
エラーの原因がわかったかどうかですが、「わかった気がしていたが、odaさんのコメントを拝見して自信がなくなった」という感じです。。。
レビュー依頼時点と、odaさんのコメントを見た後での思考を言語化してみます。
改めて、現状の理解(エラーの原因がわかったかどうか)を言葉にすると、「この問題については、言語化して指摘をいただいたのでわかった。が、自分で言語化できなかった以上、本当の意味でわかってないのかもしれない」という状況です。。