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

<https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/>

## step1(まず通す)

「次とその次を見て、値が重複していたらその値を覚えておき、ちがう値が出てくるまで次を繋ぎ替え続ける。重複してなかったら一つ進む」という考え方。スタートから重複する可能性もあるのでhead の前にダミーヘッドを用意してheadから重複判定を開始する必要がある。ダミーヘッドは本当の head と値が重複しないように `-100 <= Node.val <= 100` の外である必要がある。

## step2(整形&他の人のコードを読む)

`does_duplicate_start` は条件分岐があってかつ return が False, True, False と並ぶのは認知不可高そう。`is None` だと長くなるが、単に `node` とか書けば短いので True になる条件一つ vs. その他は False の方がいいかも。

本体の方で、while のあとに if が来て処理が長い→else は短い処理なので、if の判定を入れ替えた方がいいかも。↑のコメントも T と F が入れ替わる形で。

変数名に略語を使うかどうかは流派によるだろうが、今回は関数名にも duplicate があるのでdup_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.

ある程度の規模の組織になると、ソースコードを書く量より読む量のほうが増えます。このとき、書くより読むのにかかるコストを下げたほうが、全体としてのコストが低くなります。読むにあたり、略語が使われていると、認知負荷が上がる場合があります。これを避けるため、フルスペルで書いたほうが良いのだと思います。

Coding Agent にコードを読み書きさせる場合はよく分からないです。フルスペルのほうが略語より、 Coding Agent がより正確に意味を読み取ってくれる気がするのですが、気のせいかもしれません。

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.

ありがとうございます。

そのお話聞いて、略語で書いても認知不可が上がらないかどうかは判断も難しいですしいちいちそれを気にするくらいなら常にフルスペルで書いた方が書く時の負担も軽いかもしれないなとも思いました。


いつ仕様が変わるかわからないのでマジックナンバー避けるべしというのも納得: <https://github.com/goto-untrapped/Arai60/pull/43#discussion_r1695376875>

if else で書くか if の中で continue して else の方は外で書くかだが、今回はネストが深くない方がいいので continue かな。

と思ったが、そもそも is_unique を関数化する意味が薄い気がして来た。関数の中身が一つの if else だけなので。そうなると、条件に否定がない方が見やすいと思うので結局元と同じ順番に戻った。

-1000 がマジックナンバーになっておりよくないので、 float("-inf") にしてみた。マイナスなのは、一応ソートされてて欲しいので。一応 int vs. float の型の違いはあるが int の最大みたいなのはないので一旦これで。

## step3(10分以内にさっとかける * 3回)

node.next が None ならその先を見る意味もないので↓でもいいかもしれない。

```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_head = ListNode(val=float("-inf"), next=head)
node = dummy_head
while node.next:
if node.next.next and node.next.val == node.next.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.

ここの比較でheadとダミーヘッドとの値の比較は行われていないので、今回ダミーヘッドの値は考慮しなくても良いかと思いました

ダミーヘッドは本当の head と値が重複しないように -100 <= Node.val <= 100 の外である必要がある。

valに-infが入っているとなにかしらに使われるのかなと思って読むので、dummy_headは以下のようにしてもよいかと思いましたが、特に今のままでも弊害はないはずです。好みかと思います。

dummy_head = ListNode(next=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.

確かにそうですね、初めに書いたコードでは dummy_head から比較していたのを head からの比較に変えた時にそこまで意識が回っていませんでした。

valに-infが入っているとなにかしらに使われるのかなと思って読むので

特殊な値を使う時は意図を持って(= 不要なら無闇に使わない)というのは一つの指針になりそうです。

ありがとうございます。

duplicate_value = node.next.val
while node.next and node.next.val == duplicate_value:
node.next = node.next.next
else:
node = node.next

return dummy_head.next

```
30 changes: 30 additions & 0 deletions 82_Rmove_Duplicates_from_Sorted_List_II/step1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Optional


# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next


class Solution:
def does_duplicate_start(self, node: Optional[ListNode]) -> bool:
if node is None or node.next is None:
return False
if node.val == node.next.val:
return True
return False

def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_head = ListNode(val=-1000, next=head)
node = dummy_head
while node is not None:
if self.does_duplicate_start(node.next):
dup_val = node.next.val
while node.next and node.next.val == dup_val:
node.next = node.next.next
else:
node = node.next

return dummy_head.next
23 changes: 23 additions & 0 deletions 82_Rmove_Duplicates_from_Sorted_List_II/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional


# 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(val=float("-inf"), next=head)
node = dummy_head
while node:
if node.next and node.next.next and node.next.val == node.next.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.

step1 のように node.next is not None と書いたほうが良いと思います。

こちらのコメントをご参照ください。
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.

ありがとうございます。
別問題でも同じコメントいただいてましたがスタイルガイドの「True/False Evaluations」のところの意図は理解したのでそのスタイルで書いてみようと思います。

duplicate_value = node.next.val
while node.next and node.next.val == duplicate_value:
node.next = node.next.next
else:
node = node.next

return dummy_head.next
23 changes: 23 additions & 0 deletions 82_Rmove_Duplicates_from_Sorted_List_II/step3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Optional


# 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(val=float("-inf"), next=head)
node = dummy_head
while node:
if node.next and node.next.next and node.next.val == node.next.next.val:
duplicate_value = node.next.val
while node.next and node.next.val == duplicate_value:
node.next = node.next.next
else:
node = node.next

return dummy_head.next