From c17c2c695dcdc77168446f7429b5cc0e22c7a72f Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Fri, 11 Apr 2025 23:49:06 -0400 Subject: [PATCH 1/5] Step 1 & Step 2 --- .../solution_ja.md | 121 +++++++++++++++++- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md index 374e0e1..90a2b1b 100644 --- a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md +++ b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md @@ -1,32 +1,143 @@ ## Problem -// The URL of the problem + +https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ ## Step 1 -5分程度答えを見ずに考えて、手が止まるまでやってみる。 + +5 分程度答えを見ずに考えて、手が止まるまでやってみる。 何も思いつかなければ、答えを見て解く。ただし、コードを書くときは答えを見ないこと。 動かないコードも記録する。 -正解したら一旦OK。思考過程もメモする。 +正解したら一旦 OK。思考過程もメモする。 ### Approach -* + +- 3 回の独立したループを実行 + - 1 回目: リストを走査して、対象ノードの値と次のノードの値とが重複している場合は、値を HashSet に格納 + - 2 回目: 再度頭から走査。先頭が重複ノードだった場合に備え、重複していないノードまで進める + - 3 回目: 改めて新しい先頭から走査。HashSets を確認しながら値重複ノードはスキップしつつ、重複がないノード同士をつなげ直す +- 感想 + - なんとなくもっと見やすくて効率の良い方法がありそうなモヤモヤがありながらも、思いつかなかったので上記の発想で走りきったという感じ ```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null) { + return null; + } + ListNode node = head; + HashSet duplicateVals = new HashSet<>(); + + while (node != null && node.next != null) { + if (node.val == node.next.val) { + duplicateVals.add(node.val); + } + node = node.next; + } + + node = head; + while (node != null && duplicateVals.contains(node.val)) { + if (node.next == null) { // All nodes are duplicated. + node = null; + break; + } + node = node.next; + } + head = node; + ListNode lastUnique = head; + if (node != null) { + node = node.next; + } + while (node != null) { + if (duplicateVals.contains(node.val)) { + lastUnique.next = null; + } else { + lastUnique.next = node; + lastUnique = node; + } + node = node.next; + } + return head; + } +} ``` ## Step 2 + 他の方が描いたコードを見て、参考にしてコードを書き直してみる。 参考にしたコードのリンクは貼っておく。 読みやすいことを意識する。 他の解法も考えみる。 +### Approach 1 + +- 3 つのポインタを使用 + - node: メインでリストを一つずつ走査するノード。head からスタート + - dummyHead: 番兵として元のリストの head の手前に配置する + - dummyHead を先頭に置くことで、本来の head 自体が削除対象となるケース(1->1->2 など)を考慮したループ処理が不要になる + - lastUnique: 重複のない最後のノードを追跡。最初は dummyHead +- リストを走査し一つ先のノードの値をチェックしていく。重複ノードはスキップ、重複なしノードには lastUnique と lastUnique.next をリンクさせる +- 上記を繰り返すことで dummyHead.next が必ず重複のないリストの先頭になる + ```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode node = head; + ListNode dummyHead = new ListNode(0, node); + ListNode lastUnique = dummyHead; + while (node != null) { + if (node.next != null && node.val == node.next.val) { + while (node.next != null && node.val == node.next.val) { + node = node.next; + } + lastUnique.next = node.next; + } else { + lastUnique = lastUnique.next; + } + node = node.next; + } + return dummyHead.next; + } +} +``` + +### Approach 2 + +- こちらは dummyHead と node の 2 人の登場人物だけで完了できる方法 +- Approach 1 とは逆の発想で、重複なしノードを次々とスキップし、その後重複が現れる限り一つ先につなぎなおす +- 以下を参考にした + - https://github.com/5ky7/arai60/pull/5/files#diff-0c860cd754249868513e4f9054206317fa33d0f548fc3896ac2b3e11822fd852R160-R179 +- 感想 + - 自らの無意識の選択をメタ認知した上で別の視点はないか探すという意識は常に持っておきたいと思いました。 + +```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode dummyHead = new ListNode(0, head); + ListNode node = dummyHead; + + while (node.next != null) { + if (node.next.next == null || node.next.val != node.next.next.val) { + node = node.next; + continue; + } + + int duplicateVal = node.next.val; + while (node.next != null && node.next.val == duplicateVal) { + node.next = node.next.next; + } + } + + return dummyHead.next; + } +} ``` ## Step 3 + 今度は、時間を測りながら、もう一回書く。 -アクセプトされたら消すを3回連続できたら問題はOK。 +アクセプトされたら消すを 3 回連続できたら問題は OK。 ```java From 25e74598b55cf706aec5e3c51cc0ed7b1db12f33 Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Sun, 13 Apr 2025 21:00:40 -0400 Subject: [PATCH 2/5] Step 3 --- .../solution_ja.md | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md index 90a2b1b..2e42c5b 100644 --- a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md +++ b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md @@ -74,7 +74,7 @@ class Solution { - 3 つのポインタを使用 - node: メインでリストを一つずつ走査するノード。head からスタート - dummyHead: 番兵として元のリストの head の手前に配置する - - dummyHead を先頭に置くことで、本来の head 自体が削除対象となるケース(1->1->2 など)を考慮したループ処理が不要になる + - 番兵 を先頭に置くことで、本来の head 自体が削除対象となるケース(1->1->2 など)を考慮したループ処理が不要になる - lastUnique: 重複のない最後のノードを追跡。最初は dummyHead - リストを走査し一つ先のノードの値をチェックしていく。重複ノードはスキップ、重複なしノードには lastUnique と lastUnique.next をリンクさせる - 上記を繰り返すことで dummyHead.next が必ず重複のないリストの先頭になる @@ -87,12 +87,12 @@ class Solution { ListNode lastUnique = dummyHead; while (node != null) { - if (node.next != null && node.val == node.next.val) { - while (node.next != null && node.val == node.next.val) { + if (node.next != null && node.val == node.next.val) { // Node is duplicate + while (node.next != null && node.val == node.next.val) { // Skip to last duplicated node node = node.next; } - lastUnique.next = node.next; - } else { + lastUnique.next = node.next; // Next of last duplicated node + } else { // Node is not Duplicate lastUnique = lastUnique.next; } node = node.next; @@ -105,11 +105,11 @@ class Solution { ### Approach 2 - こちらは dummyHead と node の 2 人の登場人物だけで完了できる方法 -- Approach 1 とは逆の発想で、重複なしノードを次々とスキップし、その後重複が現れる限り一つ先につなぎなおす +- Approach 1 とは逆の発想で、重複なしノードを一気にスキップし、その後重複が現れる限り一つ先につなぎなおすという方法 - 以下を参考にした - https://github.com/5ky7/arai60/pull/5/files#diff-0c860cd754249868513e4f9054206317fa33d0f548fc3896ac2b3e11822fd852R160-R179 - 感想 - - 自らの無意識の選択をメタ認知した上で別の視点はないか探すという意識は常に持っておきたいと思いました。 + - 自らの無意識の選択をメタ認知した上で別の視点はないか探すという意識は常に持っておきたいと思った ```java class Solution { @@ -134,11 +134,38 @@ class Solution { } ``` +### その他参考にした PR + +- https://github.com/shintaro1993/arai60/pull/7/files +- https://github.com/h1rosaka/arai60/pull/6/files +- https://github.com/shintaroyoshida20/leetcode/pull/7/files + ## Step 3 今度は、時間を測りながら、もう一回書く。 アクセプトされたら消すを 3 回連続できたら問題は OK。 +Approach 1 + ```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode node = head; + ListNode dummyHead = new ListNode(0, node); + ListNode lastUnique = dummyHead; + while (node != null) { + if (node.next != null && node.val == node.next.val) { // Node is duplicate + while (node.next != null && node.val == node.next.val) { // Skip to last duplicated node + node = node.next; + } + lastUnique.next = node.next; // Next of last duplicated node + } else { // Node is not Duplicate + lastUnique = lastUnique.next; + } + node = node.next; + } + return dummyHead.next; + } +} ``` From 0b9712f20a7839b24d2d805a121c04664649a20f Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Thu, 17 Apr 2025 16:11:15 -0400 Subject: [PATCH 3/5] Create Step4 --- .../solution_ja.md | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md index 2e42c5b..a786b33 100644 --- a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md +++ b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md @@ -169,3 +169,70 @@ class Solution { } } ``` + +## Step 4 + +> dummyHead 置かないと分岐が増えるけれども、それでも解くことはできます。 + +というコメントをいただいたので実装してみた。やはり + +```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + // Skip duplicated Node + while (head != null && head.next != null && head.val == head.next.val) { + int dupVal = head.val; + while (head != null && head.val == dupVal) { + head = head.next; + } + } + + if (head == null) return null; + + ListNode unique = head; + ListNode node = head.next; + + while (node != null && node.next != null) { + if (node.val == node.next.val) { + int dupVal = node.val; + while (node != null && node.val == dupVal) { + node = node.next; + } + unique.next = node; + } else { + unique.next = node; + unique = node; + node = node.next; + } + } + + return head; + } +} +``` + +再帰の実装 + +- 面白い。思った以上にシンプルにいけた + +```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + return deleteDuplicatesHelper(head, -101); // -101: out of val + } + + private ListNode deleteDuplicatesHelper(ListNode head, int duplicatedValue) { + if (head == null) { + return null; + } + + // Start or Continue of Duplicate + if ((head.next != null && head.val == head.next.val) || head.val == duplicatedValue) { + return deleteDuplicatesHelper(head.next, head.val); + } + + head.next = deleteDuplicatesHelper(head.next, -101); // No duplicatedValue + return head; + } +} +``` From 8f0402b50891b6a6b0ee8f63f652f8c17c75620e Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Sat, 19 Apr 2025 15:01:11 -0400 Subject: [PATCH 4/5] Improve approach 2 --- .../solution_ja.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md index a786b33..24865e4 100644 --- a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md +++ b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md @@ -134,6 +134,33 @@ class Solution { } ``` +- https://github.com/katsukii/leetcode/pull/19/files#r2050481915 + - > node.next.next == null の場合に node = node.next; で node を進めたあと、 while (node.next != null) { でループを抜けようとしている点が、ややパズルに感じました。 +- 上記のフィードバックを受け、以下改良版 + +```java +class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode dummyHead = new ListNode(0, head); + ListNode node = dummyHead; + + while (node.next != null && node.next.next != null) { + if (node.next.val != node.next.next.val) { + node = node.next; + continue; + } + + int duplicateVal = node.next.val; + while (node.next != null && node.next.val == duplicateVal) { + node.next = node.next.next; + } + } + + return dummyHead.next; + } +} +``` + ### その他参考にした PR - https://github.com/shintaro1993/arai60/pull/7/files From 87a46baaace994948f32ba73cfa6330d34b6fc74 Mon Sep 17 00:00:00 2001 From: Yusuke Katsuki Date: Sat, 19 Apr 2025 15:22:14 -0400 Subject: [PATCH 5/5] suplementary explanation --- 0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md index 24865e4..2f75511 100644 --- a/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md +++ b/0082_Remove_Duplicates_from_Sorted_List_2/solution_ja.md @@ -62,6 +62,14 @@ class Solution { } ``` +- https://github.com/katsukii/leetcode/pull/19/files#r2050477054 + - 以下の方が直感的 + +```java +lastUnique.next = node; +lastUnique = node; +``` + ## Step 2 他の方が描いたコードを見て、参考にしてコードを書き直してみる。