Skip to content

83. Remove Duplicates from Sorted List #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
82 changes: 82 additions & 0 deletions 83.Remove Duplicates from Sorted List.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# step1
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
copy = head
Copy link

Choose a reason for hiding this comment

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

copy は標準ライブラリーにある名前なので、できれば被せたくないです。
https://docs.python.org/3/library/copy.html
あとから使いたくなるかもしれないし、こちらを指していると勘違いするかもしれないからです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

たしかに、標準ライブラリと被る点は考慮漏れでした。
currentが適切だったかなと考えています

while copy and copy.next:
if copy.val == copy.next.val:
copy.next = copy.next.next
else:
copy = copy.next
return head
```
#### 思考ログ
- おおよそ解法は思いついたが時間内にコードを書けなかったのでaraiさんの解法を確認
- 現在の値と次の値を比較するところをisで書いたが今回の場合アドレスの一致ではなく値の一致を見ないとダメなので==にしないとダメ

# step2
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
copy = head
while copy and copy.next:
if copy.val == copy.next.val:
copy.next = copy.next.next
continue
copy = copy.next
return head
```
再帰でも解いてみる
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next:
Copy link

Choose a reason for hiding this comment

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

あくまで参考にはなりますが、Noneとの比較はis Noneもしくはis not Noneを使えというスタイルも存在します。

Copy link
Owner Author

Choose a reason for hiding this comment

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

あ、これは前回指摘されたところ・・・
教えていただきありがとうございます!

return head
if head.val == head.next.val:
head = self.deleteDuplicates(head.next)
else:
head.next = self.deleteDuplicates(head.next)
return head
```
#### 思考ログ
- 計算量
- time complexity:o(n)
- space complexity:o(1)
- 与えられたオブジェクトに破壊的な変更を行うのは実務ではあまりやらないので違和感があったが、コーディングテストではその辺りは気にしないでいい?
- pythonの変数から変数への代入は参照渡しになっている
- pythonのcopyについて調べてみる https://docs.python.org/ja/3/library/copy.html
- A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
- A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
- deep copyを使えば与えられたオブジェクトの破壊的な変更は防げそう
- 過去解いた人のPRを見てみる
- 再帰で解く方法もあるそう
- 普段あまり再帰でコードを書く機会がないが再帰でしか書けないコード以外で再帰を使う意味はあるのか
- 個人的には再帰に慣れていないからか読みにくい
- nodchipさんのコメントでも以下のようにあった
>あくまで個人的な意見なのですが、ループと再帰で同じ処理が実装できる場合は、ループのほうが読んでいて認知負荷が低いように思います。個人的には step2 の回答のほうが好みです。
Comment on lines +52 to +56

Choose a reason for hiding this comment

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

感想みたいなコメント失礼します。

再帰に慣れていないからか読みにくい

僕自身が「ループと関数なら、関数の方がわかりやすい」と感じていたので、「再帰に慣れていないからか読みにくい」というコメントが学びになります。ありがとうございます。

関数のデメリットとして「読み出し回数上限」や「関数呼び出しのオーバーヘッドがある」ため、どちらでもいいなら、ループを採用していいように思います。(チームメンバーに合わせる前提で。)
katataku/leetcode#7 (comment)

- 再帰での解法についてodaさんが例え話で解説していた。他のところでも自然言語で説明するということがよく挙げられているが、自分と専門家集団との差の一つはここにあるのかもしれない
- 変数名に対するodaさんのコメント
- >本当に上から読んでいって明らかならば node だけでもいいと思うんですよね。読み手に何を伝えたいか次第です。for i in range(len(array)): と書かれていたら、添字の配列であろうとほぼ確信して次にいけるわけです。これが長くても迷彩か擬態にしかなりません。
- 変数名あまり考えずにわかりやすければと長くつけることが多いが、長いことがノイズになることもある。
- 命名については過去のPRでもよく議論されているがこれはコーディングテストでも重要なポイントなのか、単に突っ込みやすいからなのか
- hayashi-ayさんのコメントにあった以下のやり方だと、elseがなくなりよりシンプルにできる
- >正常系では、1回のループでノードが1つ進むとするとif文はcontinueして、elseを消すのもありかもしれないです。
- この書き方でシンプルになったがどちらの方がわかりやすいのかは人によるかもしれない
- 議論されている箇所があった(https://github.com/tarinaihitori/leetcode/pull/3/files#r1808004503)、自分としても趣味の範囲という気がする
- pythonにはセイウチ演算子なるものがあり代入式が使えるそう、ただ認知負荷が高い気がする。
- https://peps.python.org/pep-0572/
# step3
```python
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
copy = head
Copy link

Choose a reason for hiding this comment

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

手順はわかりやすくていいと思います。

copyという変数名ですが、コピー元であるはずのheadからなるlinked-listを書き換えられてしまう違和感を感じました。

Copy link
Owner Author

@pineappleYogurt pineappleYogurt Dec 12, 2024

Choose a reason for hiding this comment

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

今回の使い方だとcurrentとかが適切だったかもしれないですね。
それか下で書いた通りdeep copyを行うかですかね。
https://github.com/pineappleYogurt/leetCode/pull/4/files#diff-cf676d39885ed53f4bdd78ac45cac8597da83e44027d741c99ec921bb306b61eR45

while copy and copy.next:
if copy.val == copy.next.val:
copy.next = copy.next.next
else:
copy = copy.next
return head
```
#### 思考ログ
- コード1行1行声に出し誰かに説明しながらコードを解いてみた
- 他の人のコードを見る中で再帰やelse無しの書き方を知ったが、結局単純なif-elseの書き方が口頭で説明しやすかった