-
Notifications
You must be signed in to change notification settings - Fork 0
206. Reverse Linked List #22
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 |
---|---|---|
@@ -1,33 +1,293 @@ | ||
## Problem | ||
// The URL of the problem | ||
|
||
https://leetcode.com/problems/reverse-linked-list/ | ||
|
||
## Step 1 | ||
5分程度答えを見ずに考えて、手が止まるまでやってみる。 | ||
|
||
5 分程度答えを見ずに考えて、手が止まるまでやってみる。 | ||
何も思いつかなければ、答えを見て解く。ただし、コードを書くときは答えを見ないこと。 | ||
動かないコードも記録する。 | ||
正解したら一旦OK。思考過程もメモする。 | ||
正解したら一旦 OK。思考過程もメモする。 | ||
|
||
### Approach 1. 配列に一旦格納 | ||
|
||
時間計算量: O(n) - 2 回走査 | ||
空間計算量: O(n) | ||
|
||
- 最初に思いついたやり方 | ||
- 各ノードを配列に一度保存して逆順に再度リンクを張っていく | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null) { | ||
return null; | ||
} | ||
|
||
ListNode node = head; | ||
List<ListNode> nodes = new ArrayList<>(); | ||
while (node != null) { | ||
nodes.add(node); | ||
node = node.next; | ||
} | ||
|
||
### Approach | ||
* | ||
ListNode reverseHead = nodes.get(nodes.size() - 1); | ||
for (int i = nodes.size() - 1; i > 0; i--) { | ||
node = nodes.get(i); | ||
node.next = nodes.get(i - 1); | ||
} | ||
nodes.get(0).next = null; | ||
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. Listに入れてあげる際(L29のwhile loop内)に |
||
return reverseHead; | ||
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. reverseHead を宣言せず、 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. たしかによく考えたらわざわざ宣言しなくても良いですね |
||
} | ||
} | ||
``` | ||
|
||
- https://github.com/katsukii/leetcode/pull/22#discussion_r2060338702 | ||
- > reverseHead を宣言せず、 return nodes.get(nodes.size() - 1); としてもよいと思いました。 | ||
- たしかにわざわざ宣言する必要はなさそう | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null) { | ||
return null; | ||
} | ||
|
||
ListNode node = head; | ||
List<ListNode> nodes = new ArrayList<>(); | ||
while (node != null) { | ||
nodes.add(node); | ||
node = node.next; | ||
} | ||
|
||
for (int i = nodes.size() - 1; i > 0; i--) { | ||
node = nodes.get(i); | ||
node.next = nodes.get(i - 1); | ||
} | ||
nodes.get(0).next = null; | ||
return nodes.get(nodes.size() - 1); | ||
} | ||
} | ||
``` | ||
|
||
- https://github.com/katsukii/leetcode/pull/22#discussion_r2055140421 | ||
- Stack の方がいいかもとのコメント。仰るとおりだと思います。 | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null) { | ||
return null; | ||
} | ||
|
||
ListNode node = head; | ||
Deque<ListNode> nodes = new ArrayDeque<>(); | ||
nodes.push(null); | ||
while (node != null) { | ||
nodes.push(node); | ||
node = node.next; | ||
} | ||
|
||
ListNode reverseHead = nodes.pop(); | ||
node = reverseHead; | ||
while (!nodes.isEmpty()) { | ||
node.next = nodes.pop(); | ||
node = node.next; | ||
} | ||
|
||
return reverseHead; | ||
} | ||
} | ||
``` | ||
|
||
## Step 2 | ||
|
||
他の方が描いたコードを見て、参考にしてコードを書き直してみる。 | ||
参考にしたコードのリンクは貼っておく。 | ||
読みやすいことを意識する。 | ||
他の解法も考えみる。 | ||
|
||
- https://github.com/shintaroyoshida20/leetcode/pull/12/files#diff-62f050819b0cae018db7450bb0f0341942d799ece31ce3d0d1d4a64d45578363R24 | ||
- 再帰で解く方法があるようなのでチャレンジしたい | ||
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. はい、Approach 5の方でトライしてみました。 |
||
- https://github.com/irohafternoon/LeetCode/pull/9/files#r2020110286 | ||
- 解き方 3 種類あるらしい | ||
- > 「リンク」を中心に見ていて、範囲内のすべてのリンクを順番にひっくり返すという方法 | ||
- > 先頭の前にダミーをつけて、先頭の次のノードをダミーの後ろに挿入していく方法 | ||
- > ひっくりかえす前の鎖と後の鎖を用意して、前のやつの先頭を後のやつの先頭につけていく方法 | ||
|
||
### Approach 2. リンクにフォーカスし順番に走査しながら逆接続する方法 | ||
|
||
時間計算量: O(n) | ||
空間計算量: O(1) | ||
|
||
- リンクを順番にひっくり返していく方法 | ||
- 単に next だと再接続前か後かがわかりづらかったので、old とつけた。この命名がレビュアーにどう思われるかは気になるところ | ||
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. oldNext が node.next ということは、新しい接続先(newNext)が prev ということでしょうか。「リンクに注目する」という意味を捉えられているかわかりませんが、自分としては nextNode などでも違和感ないかもしれないです。 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. oldNextを使うのであればprevをnewNextとしたほうが読みやすいかもしれないです。 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. ありがとうございます。いずれも参考にさせていただきます。 |
||
- 追記: 以下のようにコメントいただいた | ||
- > oldNext が node.next ということは、新しい接続先(newNext)が prev ということでしょうか。「リンクに注目する」という意味を捉えられているかわかりませんが、自分としては nextNode などでも違和感ないかもしれないです。 | ||
- > oldNext を使うのであれば prev を newNext としたほうが読みやすいかもしれないです。 | ||
- たしかに newNext はかなりありな気がする | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
|
||
ListNode prev = null; | ||
ListNode node = head; | ||
|
||
while (node != null) { | ||
// Save node and reverse | ||
ListNode oldNext = node.next; | ||
node.next = prev; | ||
// Proceed nodes | ||
prev = node; | ||
node = oldNext; | ||
} | ||
|
||
return prev; // new head | ||
} | ||
} | ||
``` | ||
|
||
- https://github.com/katsukii/leetcode/pull/22#discussion_r2055141902 | ||
- prev を lastSeen にするのはどうかとコメントいただいた。良さそう。 | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
|
||
ListNode lastSeen = null; | ||
ListNode node = head; | ||
|
||
while (node != null) { | ||
ListNode oldNext = node.next; | ||
node.next = lastSeen; | ||
lastSeen = node; | ||
node = oldNext; | ||
} | ||
|
||
return lastSeen; | ||
} | ||
} | ||
``` | ||
|
||
### Approach 3. 番兵(Sentinel Node)を使用する方法 | ||
|
||
時間計算量: O(n) | ||
空間計算量: O(1) | ||
|
||
- 一番手前となる番兵を用意し、所与のリンクリストを走査しながら番兵の直後に割り込ませていく方法 | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
|
||
ListNode dummyHead = new ListNode(0, null); | ||
ListNode node = head; | ||
|
||
while (node != null) { | ||
ListNode oldNext = node.next; | ||
node.next = dummyHead.next; | ||
dummyHead.next = node; | ||
node = oldNext; | ||
} | ||
return dummyHead.next; | ||
} | ||
} | ||
``` | ||
|
||
### Approach 4. 2 つのチェーンを用意する方法 | ||
|
||
時間計算量: O(n) | ||
空間計算量: O(1) | ||
|
||
- 空の逆順用リストの先頭用ノード(reversedHead)を用意する | ||
- 元のリストの先頭ノードを別の変数に退避し、ノードを進める | ||
- 先ほど退避した先頭ノードを元のリストから切り離し逆順用リストの先頭(rversedHead の手前)につなぎ直す | ||
- reversedHead を先頭に後退させる | ||
- これをを元のリストが null になるまで繰り返す | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
|
||
ListNode reversedHead = null; | ||
ListNode node = head; | ||
|
||
while (node != null) { | ||
// temporarily store current head | ||
ListNode oldHead = node; | ||
node = node.next; | ||
oldHead.next = reversedHead; | ||
reversedHead = oldHead; | ||
} | ||
|
||
return reversedHead; | ||
} | ||
} | ||
``` | ||
|
||
### Approach 5. 再帰的に解く方法 | ||
|
||
時間計算量: O(n) | ||
空間計算量: O(n) 再帰呼び出しのためスタックがリストの長さに比例 | ||
|
||
- 最後のノードに到達するまで再帰的に進み、そのノードを head として返す | ||
- 再帰から戻りながら、各ノードのリンクを逆向きにしていく | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
|
||
ListNode newHead = reverseList(head.next); | ||
head.next.next = head; | ||
head.next = null; | ||
|
||
return newHead; | ||
} | ||
} | ||
``` | ||
|
||
## Step 3 | ||
|
||
今度は、時間を測りながら、もう一回書く。 | ||
アクセプトされたら消すを3回連続できたら問題はOK。 | ||
アクセプトされたら消すを 3 回連続できたら問題は OK。 | ||
|
||
- Approach 2 の方法で解いた。慣れたら oldNext とか書くより普通に next でもいい気がしてきた | ||
- でも初見だとやはりわかりづらい気もする。悩ましい | ||
|
||
```java | ||
class Solution { | ||
public ListNode reverseList(ListNode head) { | ||
if (head == null || head.next == null) { | ||
return head; | ||
} | ||
Comment on lines
+276
to
+278
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. たしかになくてもいいですね。 |
||
|
||
ListNode prev = null; | ||
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. lastSeen いいですね。
この観点はなかったのでたしかにと思いました。ありがとうございます。 |
||
ListNode node = head; | ||
|
||
while (node != null) { | ||
ListNode oldNext = node.next; | ||
node.next = prev; | ||
prev = node; | ||
node = oldNext; | ||
} | ||
|
||
return prev; | ||
} | ||
} | ||
``` |
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.
Stackに入れるのでも良いかも。そちらの方が処理はシンプルになりそう。
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.
たしかにです。おっしゃるとおりかと思います。