From 7e05526dcbfcc459dc1f681aebb4b93125ce0a84 Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Thu, 12 Feb 2026 10:42:49 +0900 Subject: [PATCH 1/3] step1 --- memo.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ step1-1.py | 15 ++++++++++++++ step1-2.py | 13 ++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 step1-1.py create mode 100644 step1-2.py diff --git a/memo.md b/memo.md index 4bd0397..bf25934 100644 --- a/memo.md +++ b/memo.md @@ -1 +1,62 @@ # Step1 + +## アプローチ +* remove duplicates from sorted listとの違いは, かぶっているノードは全て消すこと. +* 二つのポインタを用意したらできそう + * 一つ目のポインタは, これまでに処理し終えたリストの終点 + * 二つ目のポインタはこれから被りがあるかみる部分. + * もう少し詳しく考える. + * 一つ目のポインタの一個先と二個先が一緒じゃない(あるいはどちらかがNone)なら削除は不要. + * 一つ目のポインタの一個先と二個先が一緒なら削除処理に移る. + * 一つ目のポインタの一個先を基準点 + * 基準点の一個先(一つ目のポインタの二個先)が基準点と同じ値である限り前に進める. + * 基準点と違う値になった場所(あるいはNoneになった場所)が, 一つ目のポインタのnextが指すノード(=A)になる. + * 仕事の引き継ぎ + * 一つ目のポインタをAに移す. +* 再帰呼び出しの解法もあるか考える. + * 先頭の要素と次の要素が違うのならば, 先頭の要素のnextを先頭の要素のnextを引数として再帰呼び出しした返り値にする. + * 先頭の要素と次の要素が一緒なら, 先頭の要素と異なる要素が出てくるまでノードをたどる. 先頭の要素と異なるノードを引数として渡した再帰関数の返り値を返す. +* 再帰とtwo pointerのどちらが良いか. + * 再帰はpythonでは関数呼び出しのオーバヘッドが大きい. + * 20nsが一回の呼び出しにかかるとして, 最大の場合でリストの長さ * 20nsかかる. + * リストの長さが 1 * 10^9 / 20 = 5 * 10^7程度の時, オーバヘッドが1sになるため無視できない. + * 今回はtwo pointerの方が適切そう. + +## Code1-1 (two poitner) + +* 汚いのでstep2で修正する. + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + processed_end = dummy_start + while processed_end is not None and processed_end.next is not None and processed_end.next.next is not None: + if processed_end.next.val != processed_end.next.next.val: + processed_end = processed_end.next + continue + delete_start = processed_end.next + delete_end = processed_end.next.next + while delete_end is not None and delete_start.val == delete_end.val: + delete_end = delete_end.next + processed_end.next = delete_end + return dummy_start.next +``` + +## Code1-2 (recursion) + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None or head.next is None: + return head + if head.val != head.next.val: + head.next = self.deleteDuplicates(head.next) + return head + delete_start = head + delete_end = head.next + while delete_end is not None and delete_end.val == delete_start.val: + delete_end = delete_end.next + return self.deleteDuplicates(delete_end) +``` \ No newline at end of file diff --git a/step1-1.py b/step1-1.py new file mode 100644 index 0000000..213f70a --- /dev/null +++ b/step1-1.py @@ -0,0 +1,15 @@ +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + processed_end = dummy_start + while processed_end is not None and processed_end.next is not None and processed_end.next.next is not None: + if processed_end.next.val != processed_end.next.next.val: + processed_end = processed_end.next + continue + delete_start = processed_end.next + delete_end = processed_end.next.next + while delete_end is not None and delete_start.val == delete_end.val: + delete_end = delete_end.next + processed_end.next = delete_end + return dummy_start.next \ No newline at end of file diff --git a/step1-2.py b/step1-2.py new file mode 100644 index 0000000..02a2390 --- /dev/null +++ b/step1-2.py @@ -0,0 +1,13 @@ +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None or head.next is None: + return head + if head.val != head.next.val: + head.next = self.deleteDuplicates(head.next) + return head + delete_start = head + delete_end = head.next + while delete_end is not None and delete_end.val == delete_start.val: + delete_end = delete_end.next + return self.deleteDuplicates(delete_end) + \ No newline at end of file From d8c4586e555c5370b37ef87472aba5bd487b6c1e Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Thu, 12 Feb 2026 11:31:22 +0900 Subject: [PATCH 2/3] step2 --- memo.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ step2-1.py | 15 +++++++++++++++ step2-2.py | 13 +++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 step2-1.py create mode 100644 step2-2.py diff --git a/memo.md b/memo.md index bf25934..9561cfc 100644 --- a/memo.md +++ b/memo.md @@ -25,6 +25,9 @@ ## Code1-1 (two poitner) * 汚いのでstep2で修正する. + * is not Noneの判定が多すぎる. + * processed_end is not Noneっていらなくねとは書いてて思ったからstep2で吟味. + * .nextが基準になっているのをずらせないか検討したい. ```python class Solution: @@ -46,6 +49,56 @@ class Solution: ## Code1-2 (recursion) +* two poiterに比べて見やすいコードになった. + * self.deleteDuplicates(None) -> Noneだが, 読む人からするとこのケースが考慮されているかわかりにくいかも?? + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None or head.next is None: + return head + if head.val != head.next.val: + head.next = self.deleteDuplicates(head.next) + return head + delete_start = head + delete_end = head.next + while delete_end is not None and delete_end.val == delete_start.val: + delete_end = delete_end.next + return self.deleteDuplicates(delete_end) +``` + +# Step2 + +## Code2-1 + +* `processed_end`を`unique`にした. +* 他の人を見る限り`dummy`は結局使わないと難しそう. +* `unique_candidate = unique.next`とすることで, 注目している基準を`unique`の次のノードにした. + * `.next.next`が出現しなくて良くなった. +* `unique`が更新されるのは, `unique = unique.next`だけだが, `while`文の条件より, `unqiue.next`は`None`でないことが保証されている. `while`の条件式に`unique is not None`は不要になるが, 読み手からはわかりづらい???でもいい書き方がわからなかったので保留. + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + unique = dummy_start + while unique.next is not None: + unique_candidate = unique.next + if unique_candidate.next is None or unique_candidate.val != unique_candidate.next.val: + unique = unique.next + else: + duplication_val = unique_candidate.val + while unique_candidate is not None and unique_candidate.val == duplication_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_start.next +``` + +## Code2-2 + +* Code1-2から変更なし + ```python class Solution: def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: diff --git a/step2-1.py b/step2-1.py new file mode 100644 index 0000000..8cc3fb0 --- /dev/null +++ b/step2-1.py @@ -0,0 +1,15 @@ +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + unique = dummy_start + while unique.next is not None: + unique_candidate = unique.next + if unique_candidate.next is None or unique_candidate.val != unique_candidate.next.val: + unique = unique.next + else: + duplication_val = unique_candidate.val + while unique_candidate is not None and unique_candidate.val == duplication_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_start.next \ No newline at end of file diff --git a/step2-2.py b/step2-2.py new file mode 100644 index 0000000..02a2390 --- /dev/null +++ b/step2-2.py @@ -0,0 +1,13 @@ +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if head is None or head.next is None: + return head + if head.val != head.next.val: + head.next = self.deleteDuplicates(head.next) + return head + delete_start = head + delete_end = head.next + while delete_end is not None and delete_end.val == delete_start.val: + delete_end = delete_end.next + return self.deleteDuplicates(delete_end) + \ No newline at end of file From 561ec92d3517f6c84a2102694a83122556e7ecae Mon Sep 17 00:00:00 2001 From: Kazuki Kitano Date: Thu, 12 Feb 2026 11:47:36 +0900 Subject: [PATCH 3/3] step3 --- memo.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ step3-1.py | 23 +++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 step3-1.py diff --git a/memo.md b/memo.md index 9561cfc..b314424 100644 --- a/memo.md +++ b/memo.md @@ -112,4 +112,90 @@ class Solution: while delete_end is not None and delete_end.val == delete_start.val: delete_end = delete_end.next return self.deleteDuplicates(delete_end) +``` + +# Step3 + +* 3回実装する中でこうしたらわかりやすいかもという風に少しずつコードが変わっていった. + +## 1回目 + +* `unique_candidate`は, `unique`の一個先と二個先が等しい場合に考えたいもの. +* そうではない場合は, `unique.next`がユニークなことが確定するので, 早めに処理を切り上げる. + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + unique = dummy_start + while unique.next is not None: + if unique.next.next is None: + return dummy_start.next + if unique.next.val != unique.next.next.val: + unique = unique.next + continue + unique_candidate = unique.next + duplication_val = unique_candidate.val + while unique_candidate is not None and unique_candidate.val == duplication_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_start.next +``` + +## 2回目 + +* `duplication_val`を`unique_candidate.val`にするのは直感に反していた. + * ユニークな候補なはずなのに, その数字がかぶっている数字とするのは変. + * `duplication_val = unique.next.val`とした. + * `unique.next`は必ず重複するノードであることが`if`文の条件からわかるのでこっちの方が直感的. + +```python +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy_start = ListNode() + dummy_start.next = head + unique = dummy_start + while unique.next is not None: + if unique.next.next is None: + return dummy_start.next + if unique.next.val != unique.next.next.val: + unique = unique.next + continue + duplication_val = unique.next.val + unique_candidate = unique.next.next + while unique_candidate is not None and unique_candidate.val == duplication_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_start.next +``` + +## 3回目 + +* `unique_candidate`文字通りユニークになり得るのは, `unique.next.next.next`から. + * `.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]: + dummy_head = ListNode() + dummy_head.next = head + unique = dummy_head + while unique.next: + if unique.next.next is None: + return dummy_head.next + if unique.next.val != unique.next.next.val: + unique = unique.next + continue + duplicated_val = unique.next.val + unique_candidate = unique.next.next.next + while unique_candidate is not None and unique_candidate.val == duplicated_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_head.next ``` \ No newline at end of file diff --git a/step3-1.py b/step3-1.py new file mode 100644 index 0000000..3afca41 --- /dev/null +++ b/step3-1.py @@ -0,0 +1,23 @@ +# 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() + dummy_head.next = head + unique = dummy_head + while unique.next: + if unique.next.next is None: + return dummy_head.next + if unique.next.val != unique.next.next.val: + unique = unique.next + continue + duplicated_val = unique.next.val + unique_candidate = unique.next.next.next + while unique_candidate is not None and unique_candidate.val == duplicated_val: + unique_candidate = unique_candidate.next + unique.next = unique_candidate + return dummy_head.next + \ No newline at end of file