アルゴリズム概要
Subsets Problemは、与えられたユニークな整数配列から全ての可能な部分集合(べき集合)を生成する問題です。バックトラッキング手法を用いることで、効率的かつ直感的に解決できます。
制約条件:
1 ≤ nums.length ≤ 10-10 ≤ nums[i] ≤ 10- 配列内の全ての数値はユニーク
- 重複する部分集合は含まない
ステップバイステップ解説
初期化
結果を格納するリストresultと、現在の部分集合を構築するための作業用リストpathを初期化します。
現在の部分集合を追加
現在のpathの状態をコピーしてresultに追加します。空の配列から開始するので、最初は空集合[]が追加されます。
result.append(path[:])
要素の探索
開始位置startから配列の末尾まで各要素を順番に探索します。これにより重複を避けつつ全ての組み合わせを生成できます。
for i in range(start, len(nums)):
要素の選択
現在の要素nums[i]をpathに追加して、その要素を含む部分集合の構築を開始します。
path.append(nums[i])
再帰呼び出し
次の位置i+1から再帰的に探索を続けます。これにより、現在の要素を含む全ての部分集合が生成されます。
backtrack(i + 1)
バックトラッキング
再帰から戻ったら、追加した要素をpathから削除します。これにより他の組み合わせを探索できるよう状態をリセットします。
path.pop()
実装コード
from typing import List
class Solution:
"""
LeetCode Subsets 問題の解決クラス
バックトラッキングによる部分集合生成
"""
def subsets(self, nums: List[int]) -> List[List[int]]:
"""
与えられた整数配列の全ての部分集合を生成する
Args:
nums (List[int]): ユニークな整数配列 (1 <= len(nums) <= 10)
Returns:
List[List[int]]: 全ての部分集合を格納したリスト
Time Complexity: O(2^n * n)
Space Complexity: O(n) - 再帰スタック + 一時リスト
"""
result: List[List[int]] = []
path: List[int] = []
def backtrack(start: int) -> None:
"""
バックトラッキング用の内部関数
Args:
start (int): 探索開始位置
"""
# 現在の部分集合を結果に追加(シャローコピー必須)
result.append(path[:])
# 残りの要素を順番に探索
for i in range(start, len(nums)):
# 要素を選択
path.append(nums[i])
# 再帰的に探索を続行
backtrack(i + 1)
# バックトラッキング: 選択を取り消し
path.pop()
# 最初の位置から探索開始
backtrack(0)
return result
# 使用例
if __name__ == "__main__":
solution = Solution()
# Test Case 1
nums1 = [1, 2, 3]
result1 = solution.subsets(nums1)
print(f"Input: {nums1}")
print(f"Output: {result1}")
print()
# Test Case 2
nums2 = [0]
result2 = solution.subsets(nums2)
print(f"Input: {nums2}")
print(f"Output: {result2}")
視覚的デモンストレーション
配列 [1, 2, 3] の部分集合生成過程
「デモ開始」ボタンをクリックしてアニメーションを開始してください
計算量分析
詳細分析
このアルゴリズムの計算量は以下のように分析できます:
n: 各部分集合をコピーする際のコスト
作業用配列pathのサイズも最大n
結果配列の総要素数
実行時間の実測例
n=3: 2³ = 8 部分集合 → ~0.01ms
n=5: 2⁵ = 32 部分集合 → ~0.1ms
n=10: 2¹⁰ = 1,024 部分集合 → ~1ms