-
Notifications
You must be signed in to change notification settings - Fork 0
82. Remove Duplicates from Sorted List II #6
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,280 @@ | ||
| # Step1 | ||
|
|
||
| かかった時間:13min | ||
|
|
||
| 計算量: 計算量: Nをノード数として | ||
|
|
||
| 時間計算量:O(N) 空間計算量:O(1) | ||
|
|
||
| ```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(val=-1, next=head) | ||
| prev = dummy | ||
| current = head | ||
| while current: | ||
| while current.next and current.val == current.next.val: | ||
| current = current.next | ||
| if prev.next == current: | ||
| prev = prev.next | ||
| current = current.next | ||
| else: | ||
| prev.next = current.next | ||
| current = current.next | ||
|
|
||
| return dummy.next | ||
| ``` | ||
| 思考ログ: | ||
| - 83と同じような方針で進めるが、ポインタが一つだと足りなそう | ||
| - そういえばdummyポインタを置いて、currentと2つを動かす方法があったなと朧げながら思い出す | ||
| - あとは(自分の中で)自然な流れをコードに落とし込む | ||
| - ```current```を使って、次のノード以降でどこまで値の重複があるか確認していく(最大最終ノードまで) | ||
| - 重複がないパターン(現在のノードと次のノードの値が異なる) | ||
| - この場合は```prev```と```current```を一つずつ進める | ||
| - 重複ありのパターン(現在のノードと次以降のノードが一つ以上重複している) | ||
| - ```current```を重複がなくなる一歩手前のノードまで進める | ||
| - ```prev.next```を```current.next```に繋ぐ | ||
| - ```current```を一つ進める | ||
| - 最初、重複ありのパターンで```prev```を進めるつもりで```prev = current```としてハマった | ||
| - LinkedListの逐次処理で何が起きているのか、よく分かっていない(のでこういうことが起きる) | ||
| - 上記のように書くと何が起きるのか、一度図示してみる(discordにあげる) | ||
|
|
||
| # Step2 | ||
|
|
||
| 講師役目線でのセルフツッコミポイント: | ||
| - 変数名をもう少しなんとかしようという件 | ||
| - ```prev```だけ略語なのもバランス悪い | ||
| - 再帰で書ける? | ||
|
|
||
| 参考にした過去ログなど: | ||
| - https://discordapp.com/channels/1084280443945353267/1228700203327164487/1228789691881623704 | ||
| - https://discordapp.com/channels/1084280443945353267/1227073733844406343/1228549047451910284 | ||
| - 再帰の実装例 | ||
| - 重複検知処理を関数として切り出す | ||
| - https://discordapp.com/channels/1084280443945353267/1221030192609493053/1225103398962204686 | ||
| - https://discordapp.com/channels/1084280443945353267/1192736784354918470/1222902863148093502 | ||
| - https://discordapp.com/channels/1084280443945353267/1200089668901937312/1206627151969787985 | ||
| - while1つで回す実装 | ||
|
|
||
| 上記を踏まえて、まず変数名を少し変えてみたバージョン | ||
| ```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_node = ListNode(-1, head) | ||
| previous_node = dummy_node | ||
| current_node = head | ||
| while current_node: | ||
| while current_node.next and current_node.val == current_node.next.val: | ||
| current_node = current_node.next | ||
|
|
||
| if previous_node.next == current_node: | ||
| previous_node = previous_node.next | ||
| current_node = current_node.next | ||
| else: | ||
| previous_node.next = current_node.next | ||
| current_node = current_node.next | ||
|
|
||
| return dummy_node.next | ||
| ``` | ||
| 思考ログ: | ||
| - 変数名が長いので```prev_node```、```curr_node```のように省略形で揃えてもいいかも | ||
|
|
||
| 漁った過去ログでもあった、whileを二重にしない方式の解法を再現してみる | ||
| ```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(-1) | ||
| previous = dummy | ||
| current = head | ||
|
Comment on lines
+102
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有難うございます! |
||
| duplicated_value = None | ||
| while current: | ||
| if current.val == duplicated_value: | ||
| current = current.next | ||
| continue | ||
| if current.next and current.val == current.next.val: | ||
| duplicated_value = current.val | ||
| continue | ||
|
|
||
| previous.next = ListNode(current.val) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここで新たなノードを作っていますが、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有難うございます。
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かに私も抜けた後に |
||
| previous = previous.next | ||
| current = current.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]: | ||
| def delete_duplicates_helper( | ||
| head: Optional[ListNode], | ||
| duplicated_val: int | ||
| ) -> Optional[ListNode]: | ||
| if not head: | ||
| return head | ||
|
|
||
| if head.val == duplicated_val: | ||
| head = delete_duplicates_helper(head.next, head.val) | ||
| elif head.next and head.val == head.next.val: | ||
| head = delete_duplicates_helper(head.next, head.val) | ||
| else: | ||
| head.next = delete_duplicates_helper(head.next, None) | ||
|
|
||
| return head | ||
|
Comment on lines
+139
to
+146
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. early returnしちゃって if head.val == duplicated_val:
return delete_duplicates_helper(head.next, head.val)
if head.next and head.val == head.next.val:
return delete_duplicates_helper(head.next, head.val)
head.next = delete_duplicates_helper(head.next, None)
return headするのはいかがでしょうか。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有難うございます。 今までearly returnが選択肢の候補に入ることがあまりなかったので、今後は意識してみようと思います。 |
||
|
|
||
| unique_head = delete_duplicates_helper(head, None) | ||
| return unique_head | ||
| ``` | ||
| 思考ログ: | ||
| - 再帰する時にどうやって重複する情報を持たせようか悩んだ | ||
| - 問題は、```head```と重複削除した```head.next```を接続する部分 | ||
| - 83と違って、今回の重複削除では重複したノードは全て削除される(よって、重複処理済みのLinkedListを見ただけでは、重複したものが何だったか伝わらない) | ||
| - 例えば、```1>1>1>2```のようなLinkedListを考えて、```1```と```1>1>2```を重複処理したものの接続を考えてみる | ||
| - この場合、後者の重複処理をすると、```2```となるので、結果は```1>2```となってしまう(期待してるのは```2```) | ||
| - なので、```1```が重複してるという情報を引き継がないとうまくいかない | ||
| - まだ再帰に苦手意識がある、もう少し数を熟そう | ||
|
|
||
| # Step3 | ||
|
|
||
| かかった時間:3min | ||
|
|
||
| ```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_node = ListNode(-1, head) | ||
| prev_node = dummy_node | ||
| curr_node = head | ||
| while curr_node: | ||
| while curr_node.next and curr_node.val == curr_node.next.val: | ||
| curr_node = curr_node.next | ||
|
|
||
| if prev_node.next == curr_node: | ||
| prev_node = prev_node.next | ||
| else: | ||
| prev_node.next = curr_node.next | ||
| curr_node = curr_node.next | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. このStep 3は読みにくいと感じました。重複値がある場合とない場合の2つを意識して読み進める必要がありますよね。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 有難うございます。 #6 (comment) 先に重複値がない場合を処理してしまって、後半で重複のある場合の処理をすることで、問題をシンプルにしていくイメージでしょうか。 |
||
|
|
||
| return dummy_node.next | ||
| ``` | ||
| 思考ログ: | ||
| - 最後の分岐の```curr_node = curr_node.next```の部分は共通なので外に出した | ||
|
|
||
| # Step4 | ||
|
|
||
| 変数名を修正 | ||
| ```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]: | ||
| # 10:41 | ||
| dummy_node = ListNode(-1, head) | ||
| prev_node = dummy_node | ||
| node = head | ||
| while node and node.next: | ||
| if node.val != node.next.val: | ||
| prev_node = prev_node.next | ||
| node = node.next | ||
| continue | ||
|
|
||
| while node.next and node.val == node.next.val: | ||
| node = node.next | ||
|
|
||
| prev_node.next = node.next | ||
| node = node.next | ||
|
|
||
| return dummy_node.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]: | ||
| # 13:26 | ||
| def delete_duplicates_helper(head, duplicated_value): | ||
| if not head: | ||
| return head | ||
|
|
||
| if (head.next and head.val == head.next.val) or \ | ||
| (head.val == duplicated_value): | ||
| return delete_duplicates_helper(head.next, head.val) | ||
|
|
||
| head.next = delete_duplicates_helper(head.next, None) | ||
| return head | ||
|
|
||
| return delete_duplicates_helper(head, None) | ||
| ``` | ||
| 思考ログ: | ||
| - early-returnができないか、考える | ||
| - ノードを飛ばす時の処理という括りでif文をまとめた | ||
| - これは可読性の観点からは分けといても良かったかもしれない | ||
|
|
||
| while一回版の書き直し(新しいオブジェクト使わないパターン) | ||
| ```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_node = ListNode(-1, head) | ||
| prev_node = dummy_node | ||
| node = head | ||
| duplicated_val = None | ||
| while node: | ||
| if node.next and node.val == node.next.val: | ||
| duplicated_val = node.val | ||
| node = node.next | ||
| continue | ||
| if node.val == duplicated_val: | ||
| node = node.next | ||
| continue | ||
|
|
||
| prev_node.next = node | ||
| prev_node = prev_node.next | ||
| node = node.next | ||
|
|
||
| prev_node.next = node | ||
| return dummy_node.next | ||
| ``` | ||
| 思考ログ: | ||
| - ここもcontinueし忘れてif-elseのでっかい塊を作りがちなので注意 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
これ、とりあえず、while 突入して、あとから何が起きたかを考えるの、素直じゃない気がします。後でどうせ分岐するならば確認してから突入したほうが分かりやすくないですか。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
有難うございます。
こんな感じで、今のノードと次のノードの値が異なる場合の判定を先に済ませる感じでしょうか。
同じノードを飛ばす処理の方に意識がいってしまって、このような構成になってしまったんだと思います。。
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
同じような話で、
こっちの方が良いですかね。