半分に 分けて 後ろを 逆にし、 連結リストを 前と 後ろから 互い 違いに並べ直す
連結リストを、
海外で定番の問題リスト 4 本には入っていません
問題
連結リストのhead を
- 節が L0、
L1、 …、 Ln の 順なら、 つなぎ直した あとは L0、 Ln、 L1、 Ln−1、 L2、 Ln−2、 … の順です - 節の
値を 書き換えず、 節の つなぎ先を 付け替えてください。 新しい 節は 作りません - 各節は
val(値) とnext(次の節) を 持ちます。 最後の 節の nextは、Python では None、JavaScript では null、Go では nilです。Go の 節は ListNodeで、Val、Nextと大文字で 始まります - 節の
数は 1 以上 5000 以下、 値は どれも 1 以上 1000 以下です - 先頭の
節は 変わらないので、 受け取った headをそのまま返します
例では、
入力と出力の例
例 1
発表の
例 2
節が
例 3
節が 2 つなら、
面接での聞かれ方
連結リストの
- 「連結リストを、
前と 後ろから 交互に 取る 順に 並べ直してください」 - 並びに
移してから 並べ直すと、 「追加の 記憶を O(1) に できますか?」と 聞かれます - 3 つの
段 (まん中を 探す、 逆に する、 交互に つなぐ ) を 関数に 分けて 書くと、 読みやすさの 点でも 話が 広がります
ミドルの
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「配列と文字列」に、
関わる学び
配列と
まず 考える手順
段 1
まずは
段 2
後ろから
段 3
後半を
段 4
前半と、
解答
Python
Python
def reorder_list(head):
slow = fast = head
while fast.next and \
fast.next.next:
slow = slow.next
fast = fast.next.next
second = slow.next
slow.next = None
prev = None
while second:
nxt = second.next
second.next = prev
prev = second
second = nxt
first, second = head, prev
while second:
a = first.next
b = second.next
first.next = second
second.next = a
first, second = a, b
return headJavaScript
JavaScript
function reorderList(head) {
let slow = head;
let fast = head;
while (fast.next
&& fast.next.next) {
slow = slow.next;
fast = fast.next.next;
}
let second = slow.next;
slow.next = null;
let prev = null;
while (second !== null) {
const nxt = second.next;
second.next = prev;
prev = second;
second = nxt;
}
let first = head;
second = prev;
while (second !== null) {
const a = first.next;
const b = second.next;
first.next = second;
second.next = a;
first = a;
second = b;
}
return head;
}Go
Go
func reorderList(
head *ListNode,
) *ListNode {
slow, fast := head, head
for fast.Next != nil &&
fast.Next.Next != nil {
slow = slow.Next
fast = fast.Next.Next
}
second := slow.Next
slow.Next = nil
var prev *ListNode
for second != nil {
nxt := second.Next
second.Next = prev
prev = second
second = nxt
}
first := head
second = prev
for second != nil {
a := first.Next
b := second.Next
first.Next = second
second.Next = a
first, second = a, b
}
return head
}最初のslow はslow.next を
計算量
- 時間は O(n) です。
まん中を 探す、 逆に する、 交互に つなぐ、の どれも 1 回なめるだけです - 使う
記憶は O(1) です。 指を 何本か 持つだけで、 節は つなぎ直しています
よく ある間違い
- 前半を
切り離し忘れる。 slow.next = Noneが無いと、 前半の 最後が 後半の 節を 指したままに なり、 つなぎ直した あとに 輪が できます。 並びに 直すと 終わりません - まん中を
探す条件で fast.next.nextだけを見る。 節が 1 つだとfast.nextが空で、 その先を 読もうとして 止まります。 fast.nextがあるかを 先に 確かめます。 節が 1 つなら 後半は 空で、 そのまま headを返します - 交互に
つなぐ とき、 次の 節を 先に 取っておかない。 first.nextを書き換える 前に 元の 次の 節を aに持っておかないと、 前半の 残りを 見失います - 値を
書き換えて並べる。 値を 並びに 集めて 書き戻すと 答えの 並びは 合いますが、 「節の つなぎ先を 付け替える」という 条件に 合いません
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「後ろから
取る 順は、 後ろ半分を 逆に した順です。 遅い指と 速い指で まん中を 探して 2 本に 切り、 後半を 逆に してから、 前半と 後半を 1 つずつ 交互に つなぎます。 前半を 同じ 長さか 1 つ 長くしておけば、 後半が 尽きた所で 終わります。 時間は O(n)、 記憶は O(1) です。」
書き終えたら、
「例 1 では、
遅い 指が 30 で 止まり、 前半は 10、 20、 30、 後半は 40、 50 です。 後半を 逆に して 50、 40。 交互に つなぐと 10、 50、 20、 40 と なり、 後半が 尽きて、 前半の 残りの 30 が 後ろに 付いたままなので、 10、 50、 20、 40、 30 です。」