Skip to content
Open
Show file tree
Hide file tree
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
35 changes: 35 additions & 0 deletions 92.ReverseLinkedListII/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## ステップ1
一旦全てのnodeを分解して、つなぎ合わせる。

Choose a reason for hiding this comment

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

ノードの繋ぎ直す順番を管理して、その順番通りに繋ぎ直すという手法が分かりやすくて参考になりました!

1からスタートするindexとnodeをmap内に記録して下記の3段階で繋げる。
・leftまでは昇順
・left ~ rightは降順
・rightの次から、最後までは昇順

Choose a reason for hiding this comment

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

昇順かどうかの制約ってありましたっけ?
指定されたインデックスの範囲[left, right]のノードの繋ぎ方を逆にする、という話ではないでしょうか。

Copy link
Owner Author

@Ryotaro25 Ryotaro25 Mar 28, 2025

Choose a reason for hiding this comment

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

@TORUS0818

レビューありがとうございます。自分が初めに行った方法は、listに含まれるnode全てにindexを付与して新たにリストを作るイメージにしました。
leftまではindexの昇順、left〜right間は降順、rightの次から最後の要素まではindexの昇順でならべる。

Choose a reason for hiding this comment

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

ありがとうございます。
昇順というとnodeのvalueをイメージしてしまったのですが、indexならそうですね。

時間計算量O(n)
空間計算量O(n)
Copy link

Choose a reason for hiding this comment

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

追加の空間計算量 O(1) で実装することはできますか?


## ステップ2
step2_1.cpp
step1に変数名の変更とコメントを追加したもの。

step2_2.cpp
stackを使った解法。
step2_1のmapを使った解法との違いは、left ~ right の部分だけをstackに保存し、
元のつながりを維持しながら反転部分だけつなぎ直している。
時間計算量O(n)
空間計算量O(n)
空間計算量は同じでもleft ~ rightが小さいとこちらの方が効率的か。

step2_3.cpp
step2_2のstackを使っている解法から直接nodeをつなぎ合わす方式に変更したもの
時間計算量O(n)
空間計算量O(1)
mapやstackを使わないので改善されているか

一つのループ内でnodeが3種類出てくるので処理を追うのが少し大変
近年のPCスペックだと空間計算量に対してそこまでシビアになる必要がないと聞くこともあるので
Copy link

Choose a reason for hiding this comment

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

これはそうだと思います。
一応、どれくらいのメモリーが必要か見積もってみましょう。

(出題意図に繋ぎ変えのお手玉ができるかはありそうですが。)

mapやstackに入れて管理した方が読みやすいと感じた。

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**


54 changes: 54 additions & 0 deletions 92.ReverseLinkedListII/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if (!head) {
return nullptr;
}

int node_count = 1;

Choose a reason for hiding this comment

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

node_countをindex_to_nodeという名前のmapに入れて検索していると思うので、この変数はnode_indexみたいな名前の方が対応があるのかな、と思いました

map<int, ListNode*> index_to_node;

Choose a reason for hiding this comment

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

indexは1から順に並ぶので、mapじゃなくて配列でも同様のことができると思いました

auto node = head;
Copy link

@irohafternoon irohafternoon Mar 28, 2025

Choose a reason for hiding this comment

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

auto型は、どのような時に使うのか教えていただきたいです!
(私はC++初心者で、とりあえず型宣言できるものは全てしとこう、くらいの温度感なので)

while (node) {
index_to_node[node_count] = node;
node_count++;
node = node->next;
}

ListNode dummy_head;
node = &dummy_head;

int current_index = 1;

Choose a reason for hiding this comment

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

current_index の意味は「今見ているノードの次のindex」という感じですかね?(nodeがdummyからスタートしているので)その意味だとcurrentとついているのは少し違和感がありました

while (current_index < left) {
node->next = index_to_node[current_index];
current_index++;
node = node->next;
}

current_index = right;
while (current_index >= left) {
node->next = index_to_node[current_index];
current_index--;
node = node->next;
}

current_index = right + 1;
while (current_index < node_count) {
node->next = index_to_node[current_index];
current_index++;
node = node->next;
}
node->next = nullptr;

return dummy_head.next;
}
};
57 changes: 57 additions & 0 deletions 92.ReverseLinkedListII/step2_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if (!head) {
return nullptr;
}

int num_nodes = 1;
map<int, ListNode*> index_to_node;
auto node = head;
while (node) {
index_to_node[num_nodes] = node;
num_nodes++;
node = node->next;
}

ListNode dummy_head;
node = &dummy_head;

// leftまでを昇順に繋げる
int index = 1;
while (index < left) {
node->next = index_to_node[index];
index++;
node = node->next;
}

// left ~ rightは降順に繋げる
index = right;
while (index >= left) {
node->next = index_to_node[index];
index--;
node = node->next;
}

// right + 1 から最後までは昇順に繋げる
index = right + 1;
while (index < num_nodes) {
node->next = index_to_node[index];
index++;
node = node->next;
}
node->next = nullptr;

return dummy_head.next;
}
};
50 changes: 50 additions & 0 deletions 92.ReverseLinkedListII/step2_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if (!head) {
return nullptr;
}

ListNode dummy_head;
dummy_head.next = head;
ListNode* node = &dummy_head;

// 探索中のnodeをleftの直前まで移動
for (int i = 0; i < left - 1; i++) {
node = node->next;
}

// leftからrightの間を逆順に繋ぐため直前のnodeを記録
ListNode* prev_left = node;

// 探索中のnodeをleftの位置へ移動
node = node->next;
stack<ListNode*> reversed_nodes;
for (int i = left; i <= right; i++) {
reversed_nodes.push(node);
node = node->next;
}

// right + 1にあるnodeを記録
ListNode* current_node = node;
while (!reversed_nodes.empty()) {
prev_left->next = reversed_nodes.top();
prev_left = prev_left->next;
reversed_nodes.pop();
}

prev_left->next = current_node;

return dummy_head.next;
}
};
41 changes: 41 additions & 0 deletions 92.ReverseLinkedListII/step2_3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if (!head) {
return nullptr;
}

ListNode dummy_head;
dummy_head.next = head;
ListNode* node = &dummy_head;

// 探索中のnodeをleftの直前まで移動
for (int i = 0; i < left - 1; i++) {
node = node->next;
}

// leftの位置のnode
ListNode* reversed = node->next;

for (int i = left; i < right; i++) {
// reversedの次を取り出し、reversedの次に、次の次のnodeを繋げることで
// 取り出したnodeを一時的にリストから外す
ListNode* next_node = reversed->next;
reversed->next = next_node->next;
// 初回はnode->nextはleftの先頭にいる
next_node->next = node->next;
node->next = next_node;
Copy link

@fuga-98 fuga-98 Apr 2, 2025

Choose a reason for hiding this comment

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

nodeの役割が変わるのが気になりました。
前のforのように動いてほしいなと感じました。

(繋ぎ方が複数あるみたいなのでイメージと違っていたらすみません)
left -1 のnodeをfront_reversedなどとして、
nodeをfront_reversed
next_nodeをnodeにするのはどうでしょうか。

}
return dummy_head.next;
}
};
54 changes: 54 additions & 0 deletions 92.ReverseLinkedListII/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
if (!head) {
return nullptr;
}

int num_nodes = 1;
map<int, ListNode*> index_to_node;
ListNode* node = head;
while (node) {
index_to_node[num_nodes] = node;
num_nodes++;
Copy link

Choose a reason for hiding this comment

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

num_nodesだとnode全体の数という感じがしました。
node_countのほうが好みです

node = node->next;
}

ListNode dummy_head;
node = &dummy_head;

int index = 1;
while (index < left) {
node->next = index_to_node[index];
index++;
node = node->next;
}

index = right;
while (index >= left) {
node->next = index_to_node[index];
index--;
node = node->next;
}

index = right + 1;
while (index < num_nodes) {
node->next = index_to_node[index];
index++;
node = node->next;
}
node->next = nullptr;

return dummy_head.next;
}
};
Copy link

Choose a reason for hiding this comment

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

なぜlistではだめなのだろうかと思いましたが、読みやすいです。