Skip to content
Open
Show file tree
Hide file tree
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
117 changes: 117 additions & 0 deletions problems/82.remove-duplicates-from-sorted-list-ii/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
##step1

- 答えになる連結リストを空で用意しておいて、重複しているやつを除くのでnodeを精査した上でokになったら、末尾に足していく方針が自然だと思った
- 精査する際には調べているNodeの値と、次のNodeの値を比較して異なっていた場合調べているNodeは末尾に追加していいNodeとする(条件1)
- これではダメで1,2,2,3 の場合に2回目の2を通してしまう
- 最後に出てきたNodeの値を保持しておいて現在のNodeの値と比較し異なるという条件も必要(条件2)
- headは最初に出てきた時に保持しておきたい
- 最後は今まで出てきた数字と異なっていたら足すようにする
- 最後だけ別で処理するの綺麗じゃない気がする
- 末尾を特別に処理するのではなく,条件1を次のNodeが存在しない場合はTrueとすれば良さそう
- 条件2を満たさない場合は全てダメなのでこっちを先に処理した方が綺麗になりそう
- 同じ変数名を使い回したらだめかもしれない
- 下記のように書きたいが初回のheadのnextが繋がっていかない
```
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
targetNode = 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.

こちらのコメントをご参照ください。
tNita/arai60#3 (comment)

Copy link
Copy Markdown
Owner Author

@xbam326 xbam326 Dec 30, 2025

Choose a reason for hiding this comment

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

こちら元々の関数名がcamelCaseだったので踏襲していたのですが、元々Pythonではsnake_caseが一般的と私も認識しており、実際のコーディング面接の機会になった際に慣れでcamelCaseで書いてしまうと不要な問答が生じてしまいそうなので今後snake_caseで書くようにいたします。

distinctNumNode = None
distinctNumNodeHead = None
lastVal = -float("inf")

while targetNode:
if targetNode.val != lastVal:
if targetNode.next is None or targetNode.val != targetNode.next.val:
distinctNumNode = ListNode(targetNode.val)
# headが見つかっていない場合はheadに保持しておく
if distinctNumNodeHead is None:
distinctNumNodeHead = distinctNumNode
else:
distinctNumNode.next = ListNode(targetNode.val)
distinctNumNode = distinctNumNode.next
lastVal = targetNode.val
targetNode = targetNode.next

return distinctNumNodeHead
```
したでheadから繋がるようになったが1,2,2の時に1,2,2のWAが出る、
```
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
targetNode = head
lastVal = -float("inf")
distinctNumNodeHead = None

while targetNode:
if targetNode.val != lastVal:
if targetNode.next is None or targetNode.val != targetNode.next.val:
# headが見つかっていない場合はheadに保持しておく
if distinctNumNodeHead is None:
distinctNumNode = targetNode
distinctNumNodeHead = targetNode
else:
distinctNumNode.next = targetNode
distinctNumNode = distinctNumNode.next
lastVal = targetNode.val
targetNode = targetNode.next

return distinctNumNodeHead
```

変数で値がどの時点でどこまで保持されているかの理解が浅く、nodeをうまく繋げられていない
上でdistinctNumNodeHead.next = Noneとするとheadから繋がらない😭😭😭

一回他の人のコードを読んだ
https://github.com/Hiroto-Iizuka/coding_practice/pull/4/files
僕は答えのheadが何になるかわからなかったのでdistinctNumNodeHeadをNoneで初期化して、最初のnodeが見つかった時に入れるようにしたが、最初に答えを保持するための連結リストを作って最後に最初に作った連結リストの.nextから返すのはいいアイデアだと思った
答えのheadかどうかで場合分けする必要がなくなるので処理が楽になる

一旦自分の解放の延長線上でどこが悪いのかをChatGPTできいた
```
if distinctNumNodeTail:
distinctNumNodeTail.next = None
```
付け替えた後に、元々付け替えた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.

これ要するに不変条件はなにかということです。
引き継ぎの時に何を約束しているのかという話を延々しています。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.xzxd7jwvkwc5


##step2

一旦、dummyを定義してreturn dummy.nextで書き換える
自分の考えかたを再整理
- 出てきた最後の値と現在調べているnodeの値を比較して異なっていたらokでそれ以外はng
- 上記の条件に追加して次のNodeが存在しない場合or次のNodeと現在調べているnodeの値が異なっていたらok
- 処理が終わった後のnodeで.nextをNoneにする

改めて他の人のコードを見る
https://github.com/Hiroto-Iizuka/coding_practice/pull/4/files
正直何をしているかよくわからなかった
- 連結リストのでのつながりみたいなのが追えず、、、
https://github.com/tNita/arai60/pull/5/files
こちらの方のコードは読めた
やり方が似ていたから?
こちらの方についていたコメントで下記のやり取りは勉強になった。
処理済みか処理対象なのかを変数名で表現できていると読みやすくなるので今後は意識的にやっていきたい。
resultの他にfilterdとか(自分の場合は今回無意識にdistinctとしたが)をつけるようにしたい
```
tail は処理済みのノードを繋げていく先だということが、名前に情報をもう少し付け足せると親切に思いました(中盤で初めてそうだと分かる。それまでは node とセットで探索につかうポインタなのかな?とも思いました)

確かにそう見えますね。ご指摘ありがとうございます。
以下のようにすると探索には使わないよということがはっきりしますかね。
result_dummy = ListNode()
result_tail = result_dummy
node = head
```
https://github.com/mamo3gr/arai60/pull/4/files
こちらの方のコードを読んだ
skip_toとかlast_fixedのような変数名はわかりやすかった
連結リストあんまり慣れてない自分にとっては気にするnodeがlast_fixedと.nextと.next.nextの3つあってlast_fixed.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.

https://1kohei1.com/leetcode/ からリンクされている解説動画が分かりやすかったです。
気にするのは last_fixed.nextlast_fixed.next.next で、重複がないことを確認できたら last_fixed をひとつ先に進めます。

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.

ありがとうございます。
リンクに動画がついていたの全く気づいていませんでした。
見たところだいぶイメージがつかめました。


自分はwhileが苦手かもしれない
自分では他の選択肢がある場合は極力書かないようにしているので出てきた時頭の中でシミュレーションがうまく動かない

一旦100%理解できてはいないが一旦自分の解法で3回書いてみる。
1周した後にまた戻ってくるようにしたい


##step3

step2で修正したコードを3回書くことで自分の頭で整理した方法をコードで表現するという部分は頭のリソースをそれほど使わずにできたように感じる
39 changes: 39 additions & 0 deletions problems/82.remove-duplicates-from-sorted-list-ii/step1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# @lc app=leetcode id=82 lang=python3
#
# [82] Remove Duplicates from Sorted List II
#

# @lc code=start
# 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]:
targetNode = head
lastVal = -float("inf")
distinctNumNodeHead = None
distinctNumNodeTail = None

while targetNode:
if targetNode.val != lastVal:
if targetNode.next is None or targetNode.val != targetNode.next.val:
# headが見つかっていない場合はheadに保持しておく
if distinctNumNodeHead is None:
distinctNumNodeHead = targetNode
distinctNumNodeTail = targetNode
else:
distinctNumNodeTail.next = targetNode
distinctNumNodeTail = distinctNumNodeTail.next
lastVal = targetNode.val
targetNode = targetNode.next

if distinctNumNodeTail:
distinctNumNodeTail.next = None

return distinctNumNodeHead


# @lc code=end
34 changes: 34 additions & 0 deletions problems/82.remove-duplicates-from-sorted-list-ii/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# @lc app=leetcode id=82 lang=python3
#
# [82] Remove Duplicates from Sorted List II
#

# @lc code=start
# 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()
distinctNode = dummy
lastVal = -float("inf")
targetNode = head

while targetNode:
if targetNode.val != lastVal:
if targetNode.next is None or targetNode.val != targetNode.next.val:
distinctNode.next = targetNode
distinctNode = distinctNode.next

lastVal = targetNode.val
targetNode = targetNode.next

distinctNode.next = None

return dummy.next


# @lc code=end
34 changes: 34 additions & 0 deletions problems/82.remove-duplicates-from-sorted-list-ii/step3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# @lc app=leetcode id=82 lang=python3
#
# [82] Remove Duplicates from Sorted List II
#

# @lc code=start
# 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()
distinctNode = dummy
targetNode = head
lastVal = -float("inf")

while targetNode:
if targetNode.val != lastVal:
if targetNode.next is None or targetNode.val != targetNode.next.val:
distinctNode.next = targetNode
distinctNode = distinctNode.next

lastVal = targetNode.val
targetNode = targetNode.next

distinctNode.next = None

return dummy.next


# @lc code=end