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
88 changes: 88 additions & 0 deletions arai60/83-remove-duplicates-from-sorted-list/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# 0. Template

## Link

## How to work on each step

- Step 1: 答えを見ずに 5 分以内に解く。わからなかったら答えを見て、開始から答えを見ないで 5 分以内に正解になるところまで行う。
- Step 2: 本協会メンバーや LeetCode の過去解答を参考にしつつ、コードを見やすくする形で整える。
- Step 3: 全部消して、10 分以内にエラーを一度も出さずに正解するのを 3 回続けて行う。
- Step 4: いただいたレビューをもとに、コードを整える。

## Comments

### Step 1

途中まで Set()を返そうとしてしまっており、5 分以内に解くことができなかった。焦らず問題文を読むことが大切だなと改めて痛感したのと、移動の隙間時間でやるものではないと思い直した。また、前回の問題を解いてから 1 週間以上時間が空いてしまったのも関係していると感じたので今後は問題を解く間隔を狭めていく。

hayashi-ay さんが行っていた 5 ステップ目も今後はやってみる。

https://hayapenguin.com/notes/Posts/2024/04/24/how-to-practice-coding-effectively#%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AA%E7%B7%B4%E7%BF%92%E6%96%B9%E6%B3%95

> 3 ステップ目と同じルールで一度解けた問題について、3 日後に挑戦します。OK なら 7 日後に挑戦します。OK なら 30 日後に挑戦して、3 連続で OK ならクリアとします。一度でも失敗したら最初からやり直します。

答えを見て、while と if-else で既存の LinkedList を書き換えるパターンで解き直した。

- 時間計算量: O(N)
- 空間計算量: O(1)

#### 参考にした回答

https://github.com/t-ooka/leetcode/pull/10

### Step 2

while と if-else で既存の LinkedList を書き換えるパターンのほかにもざっくり以下があることがわかった。

1. 二重 while で既存の LinkedList を書き換えるパターン
2. 新しい LinkedList を作成するパターン

#### 1. 二重 while で既存の LinkedList を書き換えるパターン

while と if-else を使うパターンとほぼ同じだが、while node is not None を最初の while に持ってくることで条件分岐がわかりやすくなる印象。

- 時間計算量: O(N)
- 空間計算量: O(1)

#### 2. 新しい LinkedList を作成するパターン

記述量も空間計算量も増えた。既存の LinkedList に変更を加えないで実装したい時に使う。

- 時間計算量: O(N)
- 空間計算量: O(N)

#### 参考にした回答

https://github.com/hayashi-ay/leetcode/pull/20/files
https://github.com/skypenguins/coding-practice/pull/13/files
https://github.com/tokuhirat/LeetCode/pull/3/files

#### 参考にしたコメント

変数が素直に状況を説明しおらず、巧妙に動いてしまっていると、直感的でない。例えば head という変数なのに中身が移り変わっていく場合は名実が異なっているので iterative など別の変数を使った方がわかりやすい。
https://discord.com/channels/1084280443945353267/1192736784354918470/1199759433543200819
https://discord.com/channels/1084280443945353267/1228896007279083653/1231823840355815475
https://discord.com/channels/1084280443945353267/1200089668901937312/1206180274442993694

if node だと 0 や[]の時は true になるので、それらも false として判定してほしいときは if node is not None を使うのが良さそう。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

逆になっている気がします。None, 0, [] は False とみなされます。
https://docs.python.org/3/library/stdtypes.html#truth
https://google.github.io/styleguide/pyguide.html#214-truefalse-evaluations

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/kazizi55/coding-challenges/pull/3/files#diff-f681381086b8efb4fb2a30f989bcdf016b07fa8c9207d1167321d3c62a58e6deR68-R71

=> 逆。if node だと 0、[]、None の時は false になる。if node is not None だと 0 や[]の時に true になる。
https://github.com/olsen-blue/Arai60/pull/3/files#r1930423910
https://docs.python.org/3/library/stdtypes.html#truth
https://google.github.io/styleguide/pyguide.html#214-truefalse-evaluations

#### 個人的な好み

- 他の回答で、冒頭に if head is None と書いて早期 return しているものがあったが、その後で while node is not None と書くので、早期 return は冗長なのではと思った。新しい LinkedList を作る場合は head を使って new 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.

早期 return でも結果が変わらない場合は消してもいいですね。読みやすさの助けになる場合には残すこともあるかもしれません。特に再帰とかです。

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 で読まなくていい場合を明示した方が読みやすくなりますね。再帰の場合だとそれがより顕著なのかなと思います。

- 他の回答で、node.next を next_node という変数に入れているものがあったが、next node なのは自明なのでやはり冗長なのではと思った。

### Step 3

3 回書いてみて、while node is not None を最初に括り出して、二重 while で書くのが直感的だと感じた。

### Step 4

以下、いただいたフィードバック:

- 元の連結リストを保持したまま重複を削除した新しい連結リストを作成する実装を目指しているのに、元の連結リストを変更してしまっている
- 説明変数を無くした方がわかりやすくなる場合もある (node = new_node.next という感じで node = node.next と対比して記述することができるようになるので、added_node は冗長)
- if node だと 0、[]、None の時は false になる。if node is None だと 0 や[]の時に true になる。
9 changes: 9 additions & 0 deletions arai60/83-remove-duplicates-from-sorted-list/step1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class RevisedSolution:
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
else:
node = node.next
return head
26 changes: 26 additions & 0 deletions arai60/83-remove-duplicates-from-sorted-list/step2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class SolutionWithDoubledLoop:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
while node is not None:
while node.next is not None and node.val == node.next.val:
node.next = node.next.next
node = node.next
return head

class SolutionWithANewLinkedList:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None:
return None
node = head
new_head = ListNode(head.val)
new_node = new_head

while node is not None and node.next is not None:
if node.val == node.next.val:
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.

こちらのコードは元の連結リストを保持したまま重複を削除した新しい連結リストを作成する実装を目指したのだと認識しています。

nodeのほうは元の連結リストを指しているのでnode.next = node.next.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.

nodeのほうは元の連結リストを指しているのでnode.next = node.next.nextと書いてしまうと既存の連結リストを変更してしまっているのではないでしょうか。

おっしゃる通りですね。元の連結リストを変更しない形で修正しました!

https://github.com/kazizi55/coding-challenges/pull/3/files#diff-153271ae97af52ab3609bb5e1cd3215deb7bc0ef64f6750b2220ed75868283f4R1-R14

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ありがとうございます、非常にシンプルかつ分かりやすいコードでとても良いと思いました!

continue
node = node.next
added_node = ListNode(node.val)
new_node.next = added_node
new_node = added_node
Comment on lines +23 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

added_nodeという変数を定義せずに単に以下のようにも書けるかなと思いました

Suggested change
added_node = ListNode(node.val)
new_node.next = added_node
new_node = added_node
new_node.next = ListNode(node.val)
new_node = new_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.

修正しました!提示頂いたやり方の方が、node = node.next と対比して書くことができるのでわかりやすいですね。
https://github.com/kazizi55/coding-challenges/pull/3/files#diff-153271ae97af52ab3609bb5e1cd3215deb7bc0ef64f6750b2220ed75868283f4R1-R14

return new_head
27 changes: 27 additions & 0 deletions arai60/83-remove-duplicates-from-sorted-list/step3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Solution1:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
while node is not None:
while node.next is not None and node.val == node.next.val:
node.next = node.next.next
node = node.next
return head

class Solution2:
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
else:
node = node.next
return 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.

良いと思います。個人的にはこちらのやり方(Solution2)が好みです。


class Solution3:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
node = head
while node is not None:
while node.next is not None and node.val == node.next.val:
node.next = node.next.next
node = node.next
return head
14 changes: 14 additions & 0 deletions arai60/83-remove-duplicates-from-sorted-list/step4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None:
return None
node = head
new_head = ListNode(head.val)
new_node = new_head

while node is not None and node.next is not None:
if node.val != node.next.val:
new_node.next = ListNode(node.next.val)
new_node = new_node.next
node = node.next
return new_head