Skip to content
Merged
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
160 changes: 160 additions & 0 deletions 83_RemoveDuplicatesFromSortedList/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# 1回目

```go
func deleteDuplicates(head *ListNode) *ListNode {
// setに全部いれる.全部取得して,小さい順に並べ直す. いやこれもやる必要はないか.
// すでにソートされているのだから,順番に舐めていって,直前とことなったらpickすればよい

node := head
var resultHead, resultTail *ListNode
var prev *ListNode
for node != nil {
if prev == nil {
resultHead = node
resultTail = node
}
if prev != nil && prev.Val != node.Val {
resultTail.Next = node
resultTail = node
}
node = node.Next
}
return resultHead
}
```
- これはfail.


# 2回目

```go
func deleteDuplicates(head *ListNode) *ListNode {
// すでにソートされているのだから,順番に舐めていって,直前とことなったらpickすればよい

node := head
var resultHead, resultTail *ListNode
var prev *ListNode
for node != nil {
if prev == nil {
n := &ListNode{Val: node.Val, Next: nil}
resultHead = n
resultTail = n
prev = node
node = node.Next
continue
}
if prev.Val == node.Val {
prev = node
node = node.Next
continue
}

n := &ListNode{Val: node.Val, Next: nil}
resultTail.Next = n
resultTail = n

prev = node
node = node.Next
}
return resultHead
}
```
- これはpass
- 1回目がfailだった原因は,resultに元のListNodeを直接いれてしまっていたことが大きい
- 後続のノードがresultにはいってしまっていた.
- コードが冗長なので,次のステップで改善する


# 3回目

```go
func deleteDuplicates(head *ListNode) *ListNode {
// すでにソートされているのだから,順番に舐めていって,直前とことなったらpickすればよい

if head == nil {
return nil
}

node := head
resultHead := &ListNode{Val: node.Val, Next: nil}
resultTail := resultHead

prev := node
node = node.Next
Copy link
Copy Markdown

@h-masder h-masder Mar 18, 2026

Choose a reason for hiding this comment

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

2回目のprev==nilの中の処理が、このタイミングでfor文の外に出たところを見ると、プログラムを書く前にもう少し作業内容をイメージしてみてもいいのかなと思います。

以下を参考にしてみて下さい。
t0hsumi/leetcode#4 (comment)

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.

ありがとうございます

リンク先読みました。まだしっくりきてないのであとでもう一度読んでみます


for node != nil {
if prev.Val != node.Val {
resultTail.Next = &ListNode{Val: node.Val, Next: nil}
resultTail = resultTail.Next
}

prev = node
node = node.Next
}
return resultHead
}
```
- ちょっとマシに.
- prevとかheadのガードとかうまいこと消せそうな


# 4回目

```go
func deleteDuplicates(head *ListNode) *ListNode {
// すでにソートされているのだから,順番に舐めていって,**直後**と異なったらpickすればよい
node := head
if node == nil {
return nil
}

resultHead := &ListNode{Val: node.Val, Next: nil}
resultTail := resultHead
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

resultHead/Tailという変数名より、より入ってくる中身に注目した名前にするとよいように感じました。
自分なら、unique_nodes/_tailなどのように付けると思います。

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.

コメントありがとうございます
たしかにresultのような実質何も言ってない命名より良さそうに思いました 改善します


for node != nil {
if node.Next != nil && node.Val != node.Next.Val {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

常に隣同士を比べていると思います。
自分なら、5回目の考え方に少し似せて書くこともできるなと思いました。
Goには詳しくないので、間違っていたらすみません。雰囲気で書きます。unique_nodes(= resultHead)・unique_nodes_tail(= resultTail)を使いますね。

unique_nodes = head
unique_nodes_tail = head
unique_node = nil
for node != nil {
  if unique_nodes_tail.Val != node.Val {
    unique_nodes_tail.Next = node
    unique_nodes_tail = unique_nodes_tail.Next;
  }
  node = node.next;
}
unique_nodes_tail.Next = nil
return unique_nodes

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.

コード例ありがとうございます
これも副作用がないパターンの 短く書ける例として良さそうでした

最後のnil代入で、headの末尾に近いところが破壊されることに自覚的であればこれも良さそうですね
ありがとうございます!

resultTail.Next = &ListNode{Val: node.Next.Val, Next: nil}
resultTail = resultTail.Next
}

node = node.Next
}
return resultHead
}
```
- あまり綺麗にならなかった
- のでLLMにアドバイスもらって,「削除なんだからin-placeでやるのが自然だろ」と言われた.いや〜そうだね.ということで次.
Copy link
Copy Markdown

@h-masder h-masder Mar 18, 2026

Choose a reason for hiding this comment

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

メンタルモデルとコードの流れがそれなりに一致しているので、私は4回目の実装でも良いと思います。

5回目の実装は 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.

ありがとうございます

今回書き上げたタイミングでは破壊的な副作用への意識は薄かったので、そこを自覚的に選択できるようにしたほうが良かったですね



# 5回目

```go
func deleteDuplicates(head *ListNode) *ListNode {
// すでにソートされているのだから,順番に舐めていって,**直後**と異なったらpickすればよい
node := head
for node != nil && node.Next != nil {
if node.Val == node.Next.Val {
// node.Nextを削除するようにリストを繋ぎ変え
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.

いいと思いました。空間計算量O(1), 時間計算量O(n)ですね。

- ので,これをなにも見ずに5回一発で通すようにした.

---

# after memo

- 直近の別の方のPRをいくつかみると,再帰や番兵と言った手法が挙げられている.これらの選択肢も挙げられるようになる必要がある.
- https://github.com/attractal/leetcode/pull/3/changes
- https://github.com/Manato110/LeetCode-arai60/pull/3/changes
- https://github.com/TakashiMITANI/SWEAssociationOnlineCodingPractice/pull/17/changes
- あとはこれまで書いてなかったけど,時間計算量や空間計算量について検討してメモしたほうがよさそうだ(間違ったら指摘してもらえる)
- 長さNのリストを一度ループするだけなので,時間計算量としてはO(n)
- in-placeにできたので,空間計算量はO(1)