アルゴリズム概要

問題の要約

3つのスレッドを同期して 010203040506... の形式で数列を出力する問題です。

入出力例

例1: n = 2

入力: n = 2
出力: "0102"

例2: n = 5

入力: n = 5
出力: "0102030405"

戦略

主要ポイント

時間計算量

O(n) - 各数字を1回ずつ処理

空間計算量

O(1) - 同期プリミティブのみ

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

Python実装

from __future__ import annotations
from typing import Callable
from threading import Lock


class ZeroEvenOdd:
    """
    Lock + Flag による軽量スレッド同期

    メモリ効率とパフォーマンスのバランスが最良
    LeetCode環境で最も安定した結果を出す実装
    """

    def __init__(self, n: int) -> None:
        """
        初期化

        Args:
            n: 出力する数字の最大値(1 to n)
        """
        self.n = n
        self.lock = Lock()
        self.flag = 0  # 0=zero待ち, 1=odd待ち, 2=even待ち

    def zero(self, printNumber: Callable[[int], None]) -> None:
        """
        0を出力するスレッド

        各数字の前に0を出力し、次のスレッド(odd/even)を起床

        Args:
            printNumber: 数字を出力するコールバック関数
        """
        for i in range(1, self.n + 1):
            # ロック取得してflag=0になるまで待機
            with self.lock:
                while self.flag != 0:
                    # 自分の番でない場合は一旦解放して再取得
                    self.lock.release()
                    self.lock.acquire()

                # 0を出力
                printNumber(0)

                # 次のスレッドを決定: i が奇数なら odd, 偶数なら even
                self.flag = 1 if i % 2 == 1 else 2

    def even(self, printNumber: Callable[[int], None]) -> None:
        """
        偶数を出力するスレッド

        2, 4, 6, ... を出力し、制御を zero に戻す

        Args:
            printNumber: 数字を出力するコールバック関数
        """
        for i in range(2, self.n + 1, 2):
            with self.lock:
                while self.flag != 2:
                    self.lock.release()
                    self.lock.acquire()

                printNumber(i)
                self.flag = 0  # zero に制御を戻す

    def odd(self, printNumber: Callable[[int], None]) -> None:
        """
        奇数を出力するスレッド

        1, 3, 5, ... を出力し、制御を zero に戻す

        Args:
            printNumber: 数字を出力するコールバック関数
        """
        for i in range(1, self.n + 1, 2):
            with self.lock:
                while self.flag != 1:
                    self.lock.release()
                    self.lock.acquire()

                printNumber(i)
                self.flag = 0  # zero に制御を戻す

フローチャート

開始 初期化 flag = 0, n = 入力値 3スレッド起動 zero, odd, even flag == 0? (zero待ち) いいえ スピンロック待機 他スレッドが実行中 → ロック解放&再取得 はい printNumber(0) 0を出力 i % 2 == 1? (奇数判定) はい flag = 1 (odd起床) いいえ flag = 2 (even起床) odd/even スレッド実行 数字出力 → flag = 0 次の i i > n 終了 凡例 開始/終了 処理 条件分岐 はい (Yes) いいえ (No)

フローの説明

  1. 初期状態: flag = 0(zero スレッドが最初に実行)
  2. zero スレッド: 0 を出力後、i が奇数なら flag = 1(odd 起床)、偶数なら flag = 2(even 起床)
  3. odd/even スレッド: 数字を出力後、flag = 0 に戻して zero に制御を渡す
  4. ループ: このサイクルを i = 1 から n まで繰り返す

計算量分析

時間計算量: O(n)

空間計算量: O(1)

実装比較

実装 メモリ 実行速度 実装難易度 推奨度
Lock + Flag ★★★★★ ★★★☆☆ 最推奨
Event ★★★★☆ ★★★★☆ 環境依存