Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
375 changes: 375 additions & 0 deletions 82. Remove Duplicates from Sorted List II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
前回の問題と異なり重複がある場合は全てのノードを削除する必要がある。headノードも削除される可能性があるのでダミーノードを用意するのが良い。
1回のループで答えのリストを作るのが難しかったので、重複のないノードをリストに詰めてその後、答えのリストを作成する流れで書いた。

1st

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
unique_nodes = []
current_node = head
while current_node:
if current_node.next and current_node.val == current_node.next.val:
current_value = current_node.val
while current_node and current_node.val == current_value:
current_node = current_node.next
continue
unique_nodes.append(current_node)
current_node = current_node.next
if not unique_nodes:
return None
sentinel = ListNode()
current_node = sentinel
for node in unique_nodes:
current_node.next = node
current_node = current_node.next
current_node.next = None
return sentinel.next
```

> 全体的に腕力で解決している感覚を持っているからです。
ワーキングメモリーをふんだんに使いながら書いている感じがします。
一般に、コードを読むのは、ワーキングメモリーを使う行為です。
変数の意味であったり、何が入っていて、どういう処理がなされて、その時点で、どういう値が入る可能性があって、この関数は、例外を投げる可能性があるんだっけないんだっけ。
そういうことを考えながら頭の中で走らせています。だから、ワーキングメモリーをさっさと開放してあげることが大事です。

> while A:
if B:
C
continue
D
これ、たしかに、
「B は気にしなくていいので C して次に行く、D が本体。」と読めるときはいいんですが、C が長いと、ワーキングメモリーを保持させた状態で、さらに複雑な思考を要求することになりますね。
逆に、D が長いと、continue にしたほうが読みやすく、どちらも短いなら、どっちでもそんなに変わりません。
今回、C に while が入っているので、continue がどのループへの continue か一瞬迷うという事情もあるように思います。

>while current_node:
if current_node.next and current_node.val == current_node.next.val:
current_node = skip_until_value_change(current_node)
continue
unique_nodes.append(current_node)
current_node = current_node.next
なので、ここを skip_until_value_change でくくると言いたいことが分かります。
もう一つ、これは current_value のことを忘れてもいいのだということが分かるという意味もあります。

>これは趣味の範囲ですが、current_node は名前として長い感じがします。
ここのループにおいては主役なんだから、主役に、他と区別するためではない形容詞がついているのは不自然なんです。
while node:
if node.next and node.val == node.next.val:
node = skip_until_value_change(node)
continue
unique_nodes.append(node)
node = node.next
>「あなたもよっぽど分らないのね。だから天璋院様の御祐筆の妹の御嫁に行った先きの御っかさんの甥の娘なんだって、先っきっから言ってるんじゃありませんか」
名前は、なんていうか、状況に応じた適切な呼び方があるわけです。長ければ可読性が高いわけじゃないです。

2nd

重複を発見したら変えるな。2重ループ。
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
last_unique_node = sentinel
current_node = head
while current_node:
if current_node.next and current_node.val == current_node.next.val:
value_to_remove = current_node.val
while current_node and current_node.val == value_to_remove:
current_node = current_node.next
continue
last_unique_node.next = current_node
last_unique_node = current_node
current_node = current_node.next
last_unique_node.next = None
return sentinel.next
```

> 2nd-1 の current_node は、last_unique_node との対比になっているので、いいかなとは思います。

上を少し変えてwhileを抜けた後にlast_unique_node.next = Noneをしなくて良いようにした版。上の方がwhileループの中のlast_unique_nodeの更新場所が散らばらないので好み。
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
sentinel.next = head
last_unique_node = sentinel
current_node = head
while current_node:
if current_node.next and current_node.val == current_node.next.val:
value_to_remove = current_node.val
while current_node and current_node.val == value_to_remove:
current_node = current_node.next
last_unique_node.next = current_node
continue
last_unique_node = last_unique_node.next
current_node = current_node.next
return sentinel.next
```

重複を発見したら申し送る。1重ループ。
こっちの方が個人的には好みかも。処理が追いやすい気がする。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
last_unique_node = sentinel
current_node = head
value_to_remove = None
while current_node:
if current_node.val == value_to_remove:
current_node = current_node.next
continue
if current_node.next and current_node.val == current_node.next.val:
value_to_remove = current_node.val
continue
last_unique_node.next = current_node
last_unique_node = last_unique_node.next
current_node = current_node.next
last_unique_node.next = None
return sentinel.next
```

3rd

`if current_node.next and current_node.val == current_node.next.val:`のif文でcurrent_nodeも動かすかは迷った。「重複するノードを見つけました、あとはよろしく」というのは見つけたんだから処理もしてくれという気持ちにもなり少し気が利かないような気もするが、あくまでも重複を見つける担当として責務を明確にしておく方が読みやすい気もする。
動かす場合は2つ一気に動かせるので`current_node = current_node.next.next`と書ける。


```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
last_unique_node = sentinel
current_node = head
value_to_remove = None
while current_node:
if current_node.val == value_to_remove:
current_node = current_node.next
continue
if current_node.next and current_node.val == current_node.next.val:
value_to_remove = current_node.val
continue
last_unique_node.next = current_node
last_unique_node = last_unique_node.next
current_node = current_node.next
last_unique_node.next = None
return sentinel.next
```

↓追加(2024/02/15)

4th
2重ループ。関数に切り出してみた。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def is_duplicated(node1, node2):
return node1 and node2 and node1.val == node2.val

def skip_nodes(start, value_to_skip):
current = start
while current:
if current.val != value_to_skip:
break
current = current.next
return current

sentinel = ListNode()
last_unique_node = sentinel
current = head
while current:
if is_duplicated(current, current.next):
current = skip_nodes(current, current.val)
last_unique_node.next = current
else:
last_unique_node.next = current
last_unique_node = last_unique_node.next
current = current.next
return sentinel.next
```

1重ループ。`need_remove` と`value_to_remove`の2変数を定義したが後者だけで十分な気がする。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def is_duplicated(node1, node2):
return node1 and node2 and node1.val == node2.val

sentinel = ListNode()
last_unique_node = sentinel
current = head
need_remove = False
value_to_remove = None
while current:
if need_remove and current.val == value_to_remove:
pass
elif is_duplicated(current, current.next):
need_remove = True
value_to_remove = current.val
else:
need_remove = False
value_to_remove = None
last_unique_node.next = current
last_unique_node = last_unique_node.next
current = current.next
last_unique_node.next = None
return sentinel.next
```

再帰でも書いた。意外とシンプル。
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def delete_duplicates(node, value_to_remove):
if node is None:
return None
elif node.val == value_to_remove:
return delete_duplicates(node.next, value_to_remove)
elif node.next and node.val == node.next.val:
return delete_duplicates(node.next, node.val)
else:
node.next = delete_duplicates(node.next, None)
return node
return delete_duplicates(head, None)
```

5th
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
prev = sentinel
current = head
while current:
if current.next and current.val == current.next.val:
value_to_remove = current.val
while current and current.val == value_to_remove:
current = current.next
prev.next = current
else:
prev.next = current
prev = prev.next
current = current.next
return sentinel.next
```

6th
二重ループ。関数に切り出したほうが見通しが良い。
whileの中のifをcontinueにするかif-elseにするのかは好みか。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def skip_duplicated_nodes(node, value_to_remove):
while node and node.val == value_to_remove:
node = node.next
return node

sentinel = ListNode()
current = head
prev = sentinel
while current:
if current.next and current.val == current.next.val:
current = skip_duplicated_nodes(current.next, current.val)
prev.next = current
continue
prev.next = current
prev = prev.next
current = current.next
return sentinel.next
```

1重ループ。うーん。if, elifの`prev.next = current`をwhileの外で`prev.next = None`にするようにすれば`current = current.next`を共通部分として括れるけど。
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
sentinel = ListNode()
current = head
prev = sentinel
value_to_remove = None
while current:
if current.val == value_to_remove:
current = current.next
prev.next = current
elif current.next and current.val == current.next.val:
value_to_remove = current.val
current = current.next
prev.next = current
else:
prev.next = current
prev = prev.next
current = current.next
return sentinel.next
```

再帰
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def delete_duplicates(node, value_to_remove):
if node is None:
return None
if node.val == value_to_remove:
return delete_duplicates(node.next, value_to_remove)
if node.next and node.val == node.next.val:
return delete_duplicates(node.next, node.val)
node.next = delete_duplicates(node.next, None)
return node

return delete_duplicates(head, None)
```

7th

2重ループ
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def skip_duplicates(node, value_to_skip):
while node and node.val == value_to_skip:
node = node.next
return node

sentinel = ListNode()
prev = sentinel
current = head
while current:
if current.next and current.val == current.next.val:
current = skip_duplicates(current.next, current.val)
continue
prev.next = current
prev = prev.next
current = current.next
prev.next = None
return sentinel.next
```


8th

元のLinkedListを破壊せずに新しいLinkedListを作る解法で解いた。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
def skip_duplicates(node, val_to_skip):
while node and node.val == val_to_skip:
node = node.next
return node

sentinel = ListNode()
prev_node_of_new_list = sentinel
node = head
while node:
if node.next and node.val == node.next.val:
node = skip_duplicates(node.next, node.val)
continue
prev_node_of_new_list.next = ListNode(val=node.val)
prev_node_of_new_list = prev_node_of_new_list.next
node = node.next
return sentinel.next
```