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
216 changes: 216 additions & 0 deletions 82. Remove Duplicates from Sorted List II.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# step1

前回の問題と似ている(https://github.com/wanwan87/LeetCode_arai60/pull/3)

今回は重複があったら重複のあった番号は全て削除する。

前回のコードを一回書いてみて、ループの条件かifの条件を見直せばOKかな?

そういえば、node.next = node.next.nextっていう処理って削除というよりも、重複を飛ばすという処理なのかな?
メモリ上には残っていそう。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

pythonはリファレンスカウントとガベージコレクションを併用してます。

今回は飛ばしたノードはリファレンスカウントがゼロになるのでメモリ上からは消されます

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

node.next = node.next.next は、 node.next の参照先を node.next.next に変更する、という処理です。結果として、元々 node.next が参照していたノードは、どこからも参照されなくなります。結果、 @kitano-kazuki さんがおっしゃった処理が行われます。
Python の変数は、メモリ上にあるオブジェクトへの参照が格納されるという点に注意が必要です。

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.

ありがとうございます、理解しました


[1,2,3,3,4,4,5]のときに、前の問題の通りの解法だと 3に到達すると[1,2,3,4,4,5]になるので、3が自分自身が重複していたかどうかを判定する必要があるので
1つ前の値も保持しておかないとダメかな?

重複している時点で、valも次の値にすればいいのかな?

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

[1,2,3,3,4,4,5]は通ったが[1,1,1,2,3]のときに挙動がおかしいし、[1,2,3,3]みたいなときに自分が消せないから値を保持する方向か。

1巡目:ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: None}}}}}

2巡目:ListNode{val: 1, next: ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: None}}}}

3巡目:ListNode{val: 2, next: ListNode{val: 2, next: ListNode{val: 3, next: None}}}


1つ前の値を保存して置く場合、以下のコードベースで考えた時に
①のようにwhileの下にsave = node入れると、if文にsave.val == node.valで1週目で引っかかっちゃう(現在の値を比較しているだけ。)
②のようにif elseをぬけたところでsaveを作って、if文をand save.val == node.valにしようかと思ったけど、if文の中でnode書き換えちゃってるから意味ないよね?

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
while node is not None and node.next is not None:
#①save =node
if node.val == node.next.val:
node.next = node.next.next
else:
node = node.next
       #②save = node
return head
```

時間かけすぎたので以下の回答を見る。curが今見ているノードで、preがcurの後ろを指している。

https://qiita.com/KueharX/items/860d1c227ddb8cba0a61

最初考えていた1つ前の値を保持していく方針と同じだがコードに落とし込めないなあ。紙に書きだして、なんとなくイメージは湧いたぐらいで手を付けている気がするので次からもう少し具体化するところを意識してみる。けどstep2以降で意識するのでもいいのかな。

>「自分で手作業でできる」「人間にやり方を説明して代わりにやってもらえる」「機械にやり方を説明して代わりにやってもらえる(おおまかに、これがコードが書けること)」の順で難しくなっていくので、より簡単なのができないのだとたぶん書けないのです。

https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.7n6wwffw10hb


```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: ListNode) -> ListNode:
if not head:
return
node = pre = ListNode(0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
hemispherium/LeetCode_Arai60#10 (comment)

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.next = cur = head
while cur.next:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
ksaito0629/leetcode_arai60#1 (comment)

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.

ありがとうございます、スタイルガイドも読んでみます

nex = cur.next
if cur.val == nex.val:
while(nex.next and nex.val == nex.next.val):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Python では while 文等の条件式に () は付けないことが多いと思います。

nex = nex.next
pre.next = cur = nex.next
if not cur:
return node.next
else:
pre,cur,nex = cur,nex,nex.next
return node.next
```

- listnode(0)はListnodeのスタート1個前のdummyを作っている。
- nodeの今の値と次の値が一致したときに、nodeの次の値とまたその次の値を順にみていくので、連続一致する限りループを続けている。
- ループを抜けたら、preのnextとcurを[1,1,1,2,3]であれば、一致しない2にする
- if not cur は curがNoneだった場合はListNodeの末尾のため、リターンして終了
- そもそも一致しない場合は次に進める
- returnは最初にlistnode(0)を入れているので、node.nextを返す

# step2
- 個人的には、while cur.next is not Noneにしたい
- cur.nextをnexに入れなおした理由はなんだろう?
 - シンプルに読みやすか
- nextのほうがわかりやすいように思えるがnexの理由はあるのだろうか
- next.nextはなおさら分かりにくいか
- nextは組み込み関数がある.next()はこのコード上では使ってないのでいいが、上書きされてしまうらしい
- https://docs.python.org/ja/3/library/functions.html#next
- elseの中で次に進めている、1行で代入を書くと変数が増えた時に見にくそうだ
- ListNode(0,next=head)にしても良い
- Listnode(0)とListnode()はは同じ、デフォルト引数が指定されているから
- この場合、node.next = headの処理が必要
- Listnode(0,head)でもよい。Listnode(next=head)でもよい

- 個人的には
- https://discord.com/channels/1084280443945353267/1195700948786491403/1197103115539841055
- とりあえず流れを読んだ。手順を素直に書き起こしてからコードにしてコードの整理をしていた。
- https://github.com/rimokem/arai60/pull/4/changes
- これも読みやすい
- https://github.com/hiro111208/leetcode/pull/4/changes#r3067679658
- 変数名とかが整理されていて読みやすい。上のやつのとif,elseが逆になっているだけかな?

```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,next=head)
current = dummy
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらのコメントをご参照ください。
riku359/LeetCode#9 (comment)

while current is not None and current.next is not None:
runner = current.next
if runner.next is not None and runner.val == runner.next.val:
dupulicated_val = runner.val
while runner is not None and dupulicated_val == runner.val:
runner = runner.next
current.next = runner
else:
current = current.next
return dummy.next
```

# step3

step2までは、runnerのループ出た後、elseでの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 = ListNode(next=head)
node = dummy
while node is not None and node.next is not None:
runner = node.next
if runner.next is not None and runner.val == runner.next.val:
dupulicated_val = runner.val
while runner is not None and runner.val == dupulicated_val:
runner = runner.next
node.next = runner
else:
node = runner
return dummy.next
```

スムーズに解けるようになってきた。duplicatedのスペル間違えていた。

```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(next=head)
node = dummy
while node is not None and node.next is not None:
runner = node.next
if runner.next is not None and runner.val == runner.next.val:
duplicated_val = runner.val
while runner is not None and duplicated_val == runner.val:
runner = runner.next
node.next = runner
else:
node = node.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]:
dummy = ListNode(next = head)
node = dummy
while node is not None and node.next is not None:
runner = node.next
if runner.next is not None and runner.val == runner.next.val:
duplicated_val = runner.val
while runner is not None and runner.val == duplicated_val:
runner = runner.next
node.next = runner
else:
node = node.next
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

条件分岐の前でrunner = node.nextと宣言されているので、else内でもnode = runnerで統一した方が良いと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これ自分は割とどっちでもいいかなと思いました。

runnerは値が重複した時の削除用としてifの条件分岐内でしか使われてないからです。

else内でnode.nextとすることで、削除は行わず次のノードに注目を移すんだなっていう意図が伝わる気もします

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.

ありがとうございます
どっちでも書けるなあぐらいで選択していましたが意図をもって選びたいと思います

return dummy.next
```