Partition List Algorithm

Stable Partition via Two Dummy Lists (Pure / Non-destructive)

アルゴリズム概要

連結リストを閾値 x で安定パーティションします。x 未満のノードを前に、x 以上のノードを後に配置し、各パーティション内の相対順序を保持します。

採用アルゴリズム: 非破壊コピー方式

  • 2本のリスト(less, greater_equal)を並行構築
  • 元データを変更しない(Pure関数)
  • 単一走査でO(n)時間計算量
  • 安定性を自動保証

入力例

[1,4,3,2,5,2], x = 3

出力例

[1,2,2,4,3,5]

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

Step 1: 初期化

ダミーノードとテールポインタを初期化

Step 2: Less リスト構築

x未満の要素をlessリストに追加

Step 3: GE リスト構築

x以上の要素をgeリストに追加

Step 4: リスト連結

lessリストの末尾をgeリストの先頭に接続

Step 5: 完了

最終的なパーティション済みリストを返却

Step 1: Initialization Input: 1 4 3 2 5 2 x = 3 Less (< 3): empty GE (≥ 3): empty
Step 2: Building Less List Less (< 3): 1 2 2 GE (≥ 3): 4 3 5
Step 3: Both Lists Built Less (< 3): 1 2 2 GE (≥ 3): 4 3 5
Step 4: Concatenation connect Less: 1 2 2 GE: 4 3 5
Step 5: Final Result Final Partitioned List: 1 2 2 4 3 5 ✓ Stable partition: relative order preserved ✓ All < 3 elements come first ✓ All ≥ 3 elements come after

Python実装 (LeetCode形式)

from __future__ import annotations
from typing import Optional, TYPE_CHECKING

# Pylance対応のフォールバック定義
if TYPE_CHECKING:
    class ListNode:
        def __init__(self, val: int = 0, next: Optional["ListNode"] = None) -> None: ...
        val: int
        next: Optional["ListNode"]
else:
    class ListNode:
        __slots__ = ("val", "next")
        def __init__(self, val: int = 0, next: Optional["ListNode"] = None) -> None:
            self.val = val
            self.next = next

class Solution:
    """
    Partition List (安定パーティション)
    - 非破壊(Pure):入力ノードは変更せず、新ノードを生成
    - (< x) が前、(>= x) が後。元の相対順序は維持(安定)
    """

    def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:
        """
        Args:
            head: 連結リスト先頭
            x: しきい値

        Returns:
            パーティション後の新しい連結リスト先頭(非破壊)

        Complexity:
            Time: O(n)  — n はノード数(単一走査)
            Space: O(n) — Pureのため新ノードを生成
        """
        # ダミー(番兵)とテール
        less_dummy = ListNode(0)
        ge_dummy = ListNode(0)
        lt_tail = less_dummy
        ge_tail = ge_dummy

        cur = head
        while cur is not None:
            v = cur.val  # 属性参照を一度だけ
            if v < x:
                lt_tail.next = ListNode(v)
                lt_tail = lt_tail.next
            else:
                ge_tail.next = ListNode(v)
                ge_tail = ge_tail.next
            cur = cur.next

        # 連結(less -> ge)
        lt_tail.next = ge_dummy.next
        ge_tail.next = None
        return less_dummy.next

実装のポイント

  • ダミーノード活用:分岐処理を削減し、コードを簡潔化
  • 属性アクセス最小化:cur.valを変数vに一度だけ取得
  • Pure関数:元データを変更せず、新しいノードを生成
  • 型安全性:Optional[ListNode]で null安全性を確保

アルゴリズムフローチャート

このフローチャートは、連結リストを閾値xで安定パーティションするアルゴリズムの処理フローを示しています

Partition List Algorithm Flow Partition List Algorithm Flow START Initialize dummy nodes: less_dummy, ge_dummy cur = head cur != None? (Continue loop?) cur.val < x? (Value check) Add to LESS list: lt_tail.next = ListNode(val) Add to GE list: ge_tail.next = ListNode(val) cur = cur.next Connect lists: lt_tail.next = ge_dummy.next RETURN less_dummy.next YES NO YES NO 1. Setup phase 2. Main loop 3. Value classification 4. List building 5. Final connection

Less List (< x)

閾値未満の要素を順序保持して構築

GE List (≥ x)

閾値以上の要素を順序保持して構築

Final Connection

2つのリストを連結して完成

計算量解析

時間計算量: O(n)

単一走査:元リストを1回だけ巡回
定数時間操作:各ノードに対する判定・追加処理
最適性:これ以上の効率化は不可能

空間計算量: O(n)

新ノード生成:各元ノードに対応
非破壊的:元データを保護(Pure関数)
補助構造:ダミーノード2個(定数)

アルゴリズム比較

アプローチ 時間計算量 空間計算量 特徴
非破壊コピー(採用) O(n) O(n) 安全・Pure・可読性高
破壊的再配線 O(n) O(1) 最速・省メモリ
安定ソート O(n log n) O(n) 過剰計算
逐次挿入 O(n²) O(1) 実用性低

採用理由

非破壊コピー方式は、最適な時間計算量O(n)を保ちながら、元データの安全性と高い可読性を両立。 業務開発における保守性と競技プログラミングにおける効率性の両方を満たす実装です。