ゆめさくエンジニア研究所
再帰問題 12

部分集合を全部出す

整数の​並びから​作れる​部分集合を、​決まった​順で​全部返す。​数ごとに​「入れる、​入れない」の​ 2 つに​分かれる​再帰の​練習です。

問題

互いに違う整数の並び nums を受け取ります。nums から選べる部分集合 (いくつかの数を選んだ組。1 つも選ばない空の組も含む) を全部集めて、並びにして返す関数を書いてください。

返す順は次の通りに決めます。

  • 各部分集合の中の数は、nums に出てくる順のまま並べます
  • 部分集合どうしの順は、nums の前の数から「入れない」「入れる」の順に決めていく再帰の順です。たとえば nums = [1, 2] なら [[], [2], [1], [1, 2]] になります
  • nums の長さは 0 以上 10 以下で、同じ値は 2 回出てきません

入力と出力の例

例 1

入力

nums = [4, 7]

出力

[[], [7], [4], [4, 7]]

例 2

3 つになると 8 個の部分集合ができます。前半の 4 個は 5 を入れない組、後半の 4 個は 5 を入れる組です。

入力

nums = [5, 1, 9]

出力

[[], [9], [1], [1, 9],
 [5], [5, 9], [5, 1],
 [5, 1, 9]]

例 3

空の並びでも、空の部分集合が 1 つあります。

入力

nums = []

出力

[[]]

面接での聞かれ方

面接では、問題文がもっと短く渡されることが多いです。

  • 「重ならない数の配列があります。部分集合を全部返してください」
  • 書き終えたあとに「部分集合はいくつできますか?」「計算量はどうなりますか?」と続けて聞かれます
  • 「同じ値が入っていたらどうしますか?」と、問題文に無い場合を聞かれることもあります

返す順を決めておかないと、答えの見比べができません。書き始める前に「順は問いますか?」と確かめ、決めた順を口に出してから書くと伝わりやすくなります。

まず考える手順

段 1

まずは小さい例を手で書き出してみます。[4, 7] なら、4 を入れるか入れないか、7 を入れるか入れないかで、組み合わせは 4 通りです。数が 1 つ増えるたびに、通りの数は 2 倍になります。

段 2

この「入れる、入れない」を、前の数から順に決めていく形にします。位置 i の数について 2 つに分かれ、それぞれで位置 i + 1 の数をまた 2 つに分けます。最後の数まで決め終わったところで、選んだ数の組が 1 つの部分集合になります。

段 3

この形はそのまま再帰で書けます。関数 walk(i) は「位置 i から後ろを決める」仕事にして、先に「入れない」方を呼び、次に「入れる」方を呼びます。選んだ数は 1 本の並び picked に足したり外したりして使い回し、部分集合を答えに入れるときだけ写しを取ります。

ここから​解答です。​先に​自分で​書いてから​読むと、​考え方が​残ります。

解答

Python

Python

def all_subsets(nums):
    result = []
    picked = []

    def walk(i):
        if i == len(nums):
            snap = picked[:]
            result.append(snap)
            return
        walk(i + 1)
        picked.append(nums[i])
        walk(i + 1)
        picked.pop()

    walk(0)
    return result

JavaScript

JavaScript

function allSubsets(nums) {
  const result = [];
  const picked = [];
  function walk(i) {
    if (i === nums.length) {
      result.push([...picked]);
      return;
    }
    walk(i + 1);
    picked.push(nums[i]);
    walk(i + 1);
    picked.pop();
  }
  walk(0);
  return result;
}

walk(i + 1) を「入れない」で先に呼んでいるので、問題文の順になります。入れたあとに pop で外しておくと、呼び出しから戻ったときに picked が元の姿に戻ります。

計算量

  • 時間は O(n × 2ⁿ) です。部分集合は 2ⁿ 個あり、それぞれ最大 n 個の数を写します
  • 使う記憶は、答えを除くと O(n) です。再帰の深さと picked の長さが、どちらも最大 n です
  • 答えそのものが 2ⁿ 個の組なので、これより速い書き方はありません。n が 10 なら 1024 個です

よくある間違い

  • 写さずに答えへ入れる。 result.append(picked) と書くと、全部が同じ 1 本の並びを指します。最後に picked が空に戻るので、答えは空の組ばかりになります
  • 外し忘れる。 picked.pop() を書き忘れると、入れた数が次の分かれ道まで残り、組が崩れます
  • 呼ぶ順を逆にする。「入れる」を先に呼ぶと、部分集合は全部そろっても順が問題文と変わります。決めた順に合わせて呼びます

面接での話し方の例

書き始める前に、方針を短く口に出します。

「数ごとに、入れるか入れないかの 2 通りがあるので、部分集合は全部で 2 の n 乗個です。前の数から順に 2 つに分ける再帰で書きます。入れない方を先にたどるので、空の組が最初に出ます。」

書き終えたら、例を 1 つ手でたどって見せます。

「例 1 の [4, 7] でたどります。4 を入れない道で、7 を入れないと空の組、入れると [7] です。戻って 4 を入れた道で、同じように [4] と [4, 7] が出ます。答えに入れるときは写しを取っているので、あとで picked を変えても答えは崩れません。」

解答を動かした版 Python 3.12.14Node.js 22.23.4確認日 2026年10月24日

コーディング面接問題の一覧へ戻る