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
218 changes: 218 additions & 0 deletions 82.Remove_Duplicates_from_sorted_List_II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
1st.
二つのnodeを持って置く。
前を進むnodeが今のnodeと次のnodeのvalを比べて
* 等しいとき、前のnodeのみを一つ進める
* 等しくない時、前のnodeと後ろのnodeに前のnodeから一つ進んだnodeを入れる
をすればいけると考えたがうまく実装出来なかったため解答を見た

```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_node = ListNode(0, head)
back_node = dummy_node
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backよりfastとslowで前のnodeと後ろ(進みの遅い)nodeを表したらいいかなと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます。
確かに他の皆さんのコードでもbackはあまり見なかった気がするので、一般的な名前にしたいと思います。

node = head

while node:
while node.next and node.val == node.next.val:
node = node.next
# この時点でnodeのvalとnode.next.valは違う(境目)
# ここで、
# back_nodeの次が今のnode ⇔ node.valは重複しないnode(その値を持つのは一つだけ)
# その他 ⇔ 今のnode.valは重複するnodeなのでback_nodeを次(node.next)までもっていく
if back_node.next == node:
back_node = back_node.next
else:
back_node.next = node.next

node = node.next

return dummy_node.next

```
まずdummy nodeを使うという発想がなかった
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dummy を使わなくても分岐が少し増えるだけですね。使うことはいいんですが、使わないコードも書けると思って使ってください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます。
head node が重複があるかどうかの分岐ですね。
テクニックを知らなかったから解けないではいけませんからそちらの場合でも実装しておきたいと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4th.で実装してみました。
すべて重複するnodeの場合headがなくなってしまうなど、気を付けなければならないことがあり勉強になりました。

headが変更される可能性がある今回のような場合はdummy nodeをせっていすることでそのような場合を考慮せずにすむ

2nd.
(https://github.com/SanakoMeine/leetcode/pull/5/files) を参考に一番しっくりくる解答は以下だった。
pointとしては重複があるかをまずはifでみてからwhileで進めることで、これによって重複がないときとif-elseでキレイに分けて書ける。
始めの
```Python
if not scan.next:
unique.next = scan
break
```
は下のifを
```Python
if scan.next and scan.val == scan.next.val:
```
にすることでまとめられるが、こちらの方が分かりやすく感じたのでこちらを採用した
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0, head)
unique = dummy
scan = head

while scan:
# 最後のnode
if not scan.next:
unique.next = scan
break

# 重複が一つでもあるとき最後の重複部分まですべて飛ばす
if scan.val == scan.next.val:
while scan.next and scan.val == scan.next.val:
scan.next = scan.next.next
unique.next = scan.next

# 重複がないならuniqueに追加する
else:
unique.next = scan
unique = scan

# 一つ進める
scan = scan.next

return dummy.next
```

再帰を使った解答もあった
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
return head

if head.val != head.next.val:
head.next = self.deleteDuplicates(head.next)
return head

while head.next and head.val == head.next.val:
head = head.next

return self.deleteDuplicates(head.next)
```
再帰の呼び出し回数としてはすべてのnodeのvalが異なるとき最も呼び出されて、(node数-1)回呼び出される

3rd.
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0, head)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分ならdummy_headと明記します。好みかもしれません。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます。
確かにそちらの方が意味が分かりやすいですね。
参考にさせていただきます。

unique = dummy
scan = head
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scanという単語は、https://dictionary.cambridge.org/dictionary/english/scan によれば、全体をくまなくみること、みる動作を意味するみたいです。
なので、scan自体は

node = head
while node is not None:
    ...
    node = node.next

という全体の構造を意味していると感じます。関数名としては適切かもしれませんが、scanしている対象はListNodeなので、nodeでいいと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます。
確かに見ているのはひとつひとつのnodeなのでnodeとする方が良いですね。


while scan:
if not scan.next:
unique.next = scan
break

if scan.val == scan.next.val:
while scan.next and scan.val == scan.next.val:
scan.next = scan.next.next
unique.next = scan.next
else:
unique.next = scan
unique = scan

scan = sca.next
Comment on lines +130 to +138
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分ならここの部分は以下のように書きます。

Suggested change
if scan.val == scan.next.val:
while scan.next and scan.val == scan.next.val:
scan.next = scan.next.next
unique.next = scan.next
else:
unique.next = scan
unique = scan
scan = sca.next
if scan.val != scan.next.val:
unique.next = scan
unique = unique.next
scan = scan.next
continue
duplicated_value = scan.val
while scan and scan.val == duplicated_value:
scan = scan.next
unique.next = scan

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

日本語でこの解法を説明する時に、「scan.valとscan.next.valの値が違うならuniqueにscanを追加して、unique, scan進めます。同じなら、scan.valがその値にならなくなるまでscanを進め、uniqueの末尾にscanを追加します」みたいに分けて書くのが自然だと思います。

L138のように合流させるような説明は読んでいて複雑に感じます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分のコードは、

  • if 重複があるのでその数字の部分はまるごと飛ばす
  • else 重複がないのでそのnodeをuniqueに入れる

という風にみていて、どちらもif-else終了時に、
scanとscan.nextが違う数字だがscan.nextが重複がないかはまだ分からないという同じ状態
(初期状態でいうdummyとhead)になっているので次のnodeに進める処理を共通化させました。

しかし、確かにこちらの方が素直な実装に感じます。
ありがとうございます。参考にさせていただきます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4th.で実装してみました。
duplicated_valueと置いて比較することで重複している値を探して飛ばすというニュアンスがより伝わりやすくなる気がしますね。


return dummy.next
```

4th.
reviewを参考に書き直した
コメントをくださったみなさんありがとうございました。

* dummy_headを用いない方法 (https://discord.com/channels/1084280443945353267/1334041281902547036/1335906471111688212)
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
# headが重複するnodeの場合
# 重複しない要素が先頭になるまで進める
while head and head.next and head.val == head.next.val:
while head.next and head.val == head.next.val:
head = head.next
head = head.next

# ここからの実装はheadが重複しない要素なので
# dummy_headのようにheadを使うことが出来る
# ただしheadがNoneのときがあるので注意
if not head:
return head

unique = head
node = head.next

while node and node.next:
if node.val == node.next.val:
while node.next and node.val == node.next.val:
node.next = node.next.next
unique.next = node.next
else:
unique.next = node
unique = node

node = node.next

return head
```

* より自然言語で流れを説明しやすい t0hsumiさんの解法(https://discord.com/channels/1084280443945353267/1334041281902547036/1335889669476323448)
```Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_head = ListNode(0, head)
unique = dummy_head
node = head

while node:
if not node.next:
unique.next = node
break

if node.val != node.next.val:
unique.next = node
unique = unique.next
node = node.next
continue

duplicated_value = node.val
while node and node.val == duplicated_value:
node = node.next
unique.next = node

return dummy_head.next
```