Skip to content

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
272 changes: 266 additions & 6 deletions 0206_Reverse_Linked_List/solution_ja.md
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)

- 最初に思いついたやり方
- 各ノードを配列に一度保存して逆順に再度リンクを張っていく

Choose a reason for hiding this comment

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

Stackに入れるのでも良いかも。そちらの方が処理はシンプルになりそう。

Copy link
Owner Author

Choose a reason for hiding this comment

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

たしかにです。おっしゃるとおりかと思います。


```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;

Choose a reason for hiding this comment

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

Listに入れてあげる際(L29のwhile loop内)にnextについてとりあえずnullを入れていても良いかもしれないです。

return reverseHead;
Copy link

Choose a reason for hiding this comment

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

reverseHead を宣言せず、 return nodes.get(nodes.size() - 1); としてもよいと思いました。

Copy link
Owner Author

Choose a reason for hiding this comment

The 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
- 再帰で解く方法があるようなのでチャレンジしたい
Copy link

Choose a reason for hiding this comment

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

再帰は見ておいてもいいかもしれません。

Copy link
Owner Author

Choose a reason for hiding this comment

The 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 とつけた。この命名がレビュアーにどう思われるかは気になるところ

Choose a reason for hiding this comment

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

oldNext が node.next ということは、新しい接続先(newNext)が prev ということでしょうか。「リンクに注目する」という意味を捉えられているかわかりませんが、自分としては nextNode などでも違和感ないかもしれないです。

Choose a reason for hiding this comment

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

oldNextを使うのであればprevをnewNextとしたほうが読みやすいかもしれないです。

Copy link
Owner Author

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

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

この条件はなくてもいいでしょうか。(あってもいいですが。)

Copy link
Owner Author

Choose a reason for hiding this comment

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

たしかになくてもいいですね。


ListNode prev = null;

Choose a reason for hiding this comment

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

lastSeenとかはどうでしょうか。Linked List自体にもnext, prev(今回は単方向なのでないですが)があって、結構似たような変数が増えると結構読むのが難しいなという感覚があります

Copy link
Owner Author

Choose a reason for hiding this comment

The 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;
}
}
```