アルゴリズム概要

問題説明

単方向連結リストの先頭 head と整数 left, right が与えられる。 位置 left から right までのノードを反転し、結果のリストを返して下さい。

入出力例

Input: head = [1,2,3,4,5], left = 2, right = 4
Output: [1,4,3,2,5]

Input: head = [5], left = 1, right = 1
Output: [5]

制約条件

戦略

主要ポイント

時間: O(n) 空間: O(1)

番兵ノードを使わず、left==1 を個別処理することで追加割当をゼロに抑えた最適実装。

ステップバイステップ解説

Python実装

from typing import Optional

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    """
    Reverse Linked List II
    - 一回走査・O(1)追加メモリ・番兵ノード不要
    - Time: O(n), Space: O(1)
    """

    def reverseBetween(
        self,
        head: Optional[ListNode],
        left: int,
        right: int
    ) -> Optional[ListNode]:
        # エッジケース: 空リストまたは区間長1
        if head is None or left == right:
            return head

        # Case 1) 先頭から反転(left == 1)
        if left == 1:
            prev: Optional[ListNode] = None
            curr: Optional[ListNode] = head
            # 先頭から right 個を反転
            for _ in range(right):
                next_: Optional[ListNode] = curr.next
                curr.next = prev
                prev = curr
                curr = next_
            # 旧先頭を残りに接続
            head.next = curr
            return prev  # 新しい先頭

        # Case 2) 中間以降の反転(left > 1)
        # 1) left-1 位置まで進めて pre を取得
        pre: ListNode = head
        for _ in range(1, left - 1):
            pre = pre.next

        # 2) 区間 [left, right] を反転
        start: ListNode = pre.next
        prev: Optional[ListNode] = None
        curr: Optional[ListNode] = start
        for _ in range(right - left + 1):
            next_: Optional[ListNode] = curr.next
            curr.next = prev
            prev = curr
            curr = next_

        # 3) 再接続
        pre.next = prev
        start.next = curr
        return head

フローチャート

開始 head が None または left == right はい head を返す いいえ left == 1 はい 先頭から反転 right 個分 head.next = curr prev を返す いいえ pre を移動 left - 1 の位置へ 区間を反転 [left, right] pre.next = prev start.next = curr head を返す 終了

フローの説明:
1. 空リストまたは区間長1の場合は即座に返す
2. left==1 なら先頭から right 個を反転し、旧先頭を残りに接続
3. left>1 なら pre を left-1 位置まで進める
4. 区間を反転し、pre.next と start.next で再接続
5. 元のリスト先頭を返す

計算量分析

項目 計算量 説明
時間 O(n) 一回走査で left-1 まで進み、区間を反転(最大 n 回操作)
空間 O(1) 追加ノード割当なし。ローカル変数のみ(prev, curr, next_ 等)

手法比較

手法 追加メモリ 実装複雑度 備考
原地反転(採用) O(1) 番兵ノード不要、left==1 を個別処理
番兵ノード使用 O(1) 実装容易だが1ノード分の割当
再帰反転 O(n) コールスタック深さ=区間長